debuggers.hg
changeset 13617:6c087036b3ed
[XEND] Add missing XendMonitor.py
Signed-off-by: Alastair Tse <atse@xensource.com>
Signed-off-by: Alastair Tse <atse@xensource.com>
author | Alastair Tse <atse@xensource.com> |
---|---|
date | Wed Jan 24 12:26:19 2007 +0000 (2007-01-24) |
parents | f7a52957b427 |
children | 259470f0856b |
files | tools/python/xen/xend/XendMonitor.py |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/tools/python/xen/xend/XendMonitor.py Wed Jan 24 12:26:19 2007 +0000 1.3 @@ -0,0 +1,318 @@ 1.4 +#============================================================================ 1.5 +# This library is free software; you can redistribute it and/or 1.6 +# modify it under the terms of version 2.1 of the GNU Lesser General Public 1.7 +# License as published by the Free Software Foundation. 1.8 +# 1.9 +# This library is distributed in the hope that it will be useful, 1.10 +# but WITHOUT ANY WARRANTY; without even the implied warranty of 1.11 +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1.12 +# Lesser General Public License for more details. 1.13 +# 1.14 +# You should have received a copy of the GNU Lesser General Public 1.15 +# License along with this library; if not, write to the Free Software 1.16 +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 1.17 +#============================================================================ 1.18 +# Copyright (C) 2007 XenSource Ltd. 1.19 +#============================================================================ 1.20 + 1.21 +from xen.lowlevel.xc import xc 1.22 +import time 1.23 +import threading 1.24 +import os 1.25 +import re 1.26 + 1.27 +"""Monitoring thread to keep track of Xend statistics. """ 1.28 + 1.29 +VBD_SYSFS_PATH = '/sys/devices/xen-backend/' 1.30 +VBD_WR_PATH = VBD_SYSFS_PATH + '%s/statistics/wr_req' 1.31 +VBD_RD_PATH = VBD_SYSFS_PATH + '%s/statistics/rd_req' 1.32 +VBD_DOMAIN_RE = r'vbd-(?P<domid>\d+)-(?P<devid>\d+)$' 1.33 + 1.34 +NET_PROCFS_PATH = '/proc/net/dev' 1.35 +PROC_NET_DEV_RE = r'(?P<rx_bytes>\d+)\s+' \ 1.36 + r'(?P<rx_packets>\d+)\s+' \ 1.37 + r'(?P<rx_errs>\d+)\s+' \ 1.38 + r'(?P<rx_drop>\d+)\s+' \ 1.39 + r'(?P<rx_fifo>\d+)\s+' \ 1.40 + r'(?P<rx_frame>\d+)\s+' \ 1.41 + r'(?P<rx_compressed>\d+)\s+' \ 1.42 + r'(?P<rx_multicast>\d+)\s+' \ 1.43 + r'(?P<tx_bytes>\d+)\s+' \ 1.44 + r'(?P<tx_packets>\d+)\s+' \ 1.45 + r'(?P<tx_errs>\d+)\s+' \ 1.46 + r'(?P<tx_drop>\d+)\s+' \ 1.47 + r'(?P<tx_fifo>\d+)\s+' \ 1.48 + r'(?P<tx_collisions>\d+)\s+' \ 1.49 + r'(?P<tx_carrier>\d+)\s+' \ 1.50 + r'(?P<tx_compressed>\d+)\s*$' 1.51 + 1.52 + 1.53 +VIF_DOMAIN_RE = re.compile(r'vif(?P<domid>\d+)\.(?P<iface>\d+):\s*' + 1.54 + PROC_NET_DEV_RE) 1.55 +PIF_RE = re.compile(r'peth(?P<iface>\d+):\s*' + PROC_NET_DEV_RE) 1.56 + 1.57 +# The VBD transfer figures are in "requests" where we don't 1.58 +# really know how many bytes per requests. For now we make 1.59 +# up a number roughly could be. 1.60 +VBD_ROUGH_BYTES_PER_REQUEST = 1024 * 8 * 4 1.61 + 1.62 +# Interval to poll xc, sysfs and proc 1.63 +POLL_INTERVAL = 2.0 1.64 + 1.65 +class XendMonitor(threading.Thread): 1.66 + """Monitors VCPU, VBD, VIF and PIF statistics for Xen API. 1.67 + 1.68 + Polls sysfs and procfs for statistics on VBDs and VIFs respectively. 1.69 + 1.70 + @ivar domain_vcpus_util: Utilisation for VCPUs indexed by domain 1.71 + @type domain_vcpus_util: {domid: {vcpuid: float, vcpuid: float}} 1.72 + @ivar domain_vifs_util: Bytes per second for VIFs indexed by domain 1.73 + @type domain_vifs_util: {domid: {vifid: (rx_bps, tx_bps)}} 1.74 + @ivar domain_vbds_util: Blocks per second for VBDs index by domain. 1.75 + @type domain_vbds_util: {domid: {vbdid: (rd_reqps, wr_reqps)}} 1.76 + 1.77 + """ 1.78 + def __init__(self): 1.79 + threading.Thread.__init__(self) 1.80 + self.setDaemon(True) 1.81 + self.xc = xc() 1.82 + 1.83 + self.lock = threading.Lock() 1.84 + 1.85 + # tracks the last polled statistics 1.86 + self._domain_vcpus = {} 1.87 + self._domain_vifs = {} 1.88 + self._domain_vbds = {} 1.89 + self.pifs = {} 1.90 + 1.91 + # instantaneous statistics 1.92 + self._domain_vcpus_util = {} 1.93 + self._domain_vifs_util = {} 1.94 + self._domain_vbds_util = {} 1.95 + self.pifs_util = {} 1.96 + 1.97 + def get_domain_vcpus_util(self): 1.98 + self.lock.acquire() 1.99 + try: 1.100 + return self._domain_vcpus_util 1.101 + finally: 1.102 + self.lock.release() 1.103 + 1.104 + def get_domain_vbds_util(self): 1.105 + self.lock.acquire() 1.106 + try: 1.107 + return self._domain_vbds_util 1.108 + finally: 1.109 + self.lock.release() 1.110 + 1.111 + def get_domain_vifs_util(self): 1.112 + self.lock.acquire() 1.113 + try: 1.114 + return self._domain_vifs_util 1.115 + finally: 1.116 + self.lock.release() 1.117 + 1.118 + def get_pifs_util(self): 1.119 + self.lock.acquire() 1.120 + try: 1.121 + return self.pifs_util 1.122 + finally: 1.123 + self.lock.release() 1.124 + 1.125 + def _get_vif_stats(self): 1.126 + stats = {} 1.127 + 1.128 + if not os.path.exists(NET_PROCFS_PATH): 1.129 + return stats 1.130 + 1.131 + usage_at = time.time() 1.132 + for line in open(NET_PROCFS_PATH): 1.133 + is_vif = re.search(VIF_DOMAIN_RE, line.strip()) 1.134 + if not is_vif: 1.135 + continue 1.136 + 1.137 + domid = int(is_vif.group('domid')) 1.138 + vifid = int(is_vif.group('iface')) 1.139 + rx_bytes = int(is_vif.group('rx_bytes')) 1.140 + tx_bytes = int(is_vif.group('tx_bytes')) 1.141 + if not domid in stats: 1.142 + stats[domid] = {} 1.143 + 1.144 + stats[domid][vifid] = (usage_at, rx_bytes, tx_bytes) 1.145 + 1.146 + return stats 1.147 + 1.148 + def _get_pif_stats(self): 1.149 + stats = {} 1.150 + 1.151 + if not os.path.exists(NET_PROCFS_PATH): 1.152 + return stats 1.153 + 1.154 + usage_at = time.time() 1.155 + for line in open(NET_PROCFS_PATH): 1.156 + is_pif = re.search(PIF_RE, line.strip()) 1.157 + if not is_pif: 1.158 + continue 1.159 + 1.160 + pifid = int(is_pif.group('iface')) 1.161 + rx_bytes = int(is_pif.group('rx_bytes')) 1.162 + tx_bytes = int(is_pif.group('tx_bytes')) 1.163 + stats[pifid] = (usage_at, rx_bytes, tx_bytes) 1.164 + 1.165 + return stats 1.166 + 1.167 + def _get_vbd_stats(self): 1.168 + stats = {} 1.169 + 1.170 + if not os.path.exists(VBD_SYSFS_PATH): 1.171 + return stats 1.172 + 1.173 + for vbd_path in os.listdir(VBD_SYSFS_PATH): 1.174 + is_vbd = re.search(VBD_DOMAIN_RE, vbd_path) 1.175 + if not is_vbd: 1.176 + continue 1.177 + 1.178 + domid = int(is_vbd.group('domid')) 1.179 + vbdid = int(is_vbd.group('devid')) 1.180 + rd_stat_path = VBD_RD_PATH % vbd_path 1.181 + wr_stat_path = VBD_WR_PATH % vbd_path 1.182 + 1.183 + if not os.path.exists(rd_stat_path) or \ 1.184 + not os.path.exists(wr_stat_path): 1.185 + continue 1.186 + 1.187 + 1.188 + try: 1.189 + usage_at = time.time() 1.190 + rd_stat = int(open(rd_stat_path).readline().strip()) 1.191 + wr_stat = int(open(wr_stat_path).readline().strip()) 1.192 + rd_stat *= VBD_ROUGH_BYTES_PER_REQUEST 1.193 + wr_stat *= VBD_ROUGH_BYTES_PER_REQUEST 1.194 + 1.195 + if domid not in stats: 1.196 + stats[domid] = {} 1.197 + 1.198 + stats[domid][vbdid] = (usage_at, rd_stat, wr_stat) 1.199 + 1.200 + except (IOError, ValueError): 1.201 + continue 1.202 + 1.203 + return stats 1.204 + 1.205 + def _get_cpu_stats(self): 1.206 + stats = {} 1.207 + for domain in self.xc.domain_getinfo(): 1.208 + domid = domain['domid'] 1.209 + vcpu_count = domain['online_vcpus'] 1.210 + stats[domid] = {} 1.211 + for i in range(vcpu_count): 1.212 + vcpu_info = self.xc.vcpu_getinfo(domid, i) 1.213 + usage = vcpu_info['cpu_time'] 1.214 + usage_at = time.time() 1.215 + stats[domid][i] = (usage_at, usage) 1.216 + 1.217 + return stats 1.218 + 1.219 + 1.220 + def run(self): 1.221 + 1.222 + # loop every second for stats 1.223 + while True: 1.224 + self.lock.acquire() 1.225 + try: 1.226 + 1.227 + # Calculate utilisation for VCPUs 1.228 + 1.229 + for domid, cputimes in self._get_cpu_stats().items(): 1.230 + if domid not in self._domain_vcpus: 1.231 + # if not initialised, save current stats 1.232 + # and skip utilisation calculation 1.233 + self._domain_vcpus[domid] = cputimes 1.234 + self._domain_vcpus_util[domid] = {} 1.235 + continue 1.236 + 1.237 + for vcpu, (usage_at, usage) in cputimes.items(): 1.238 + if vcpu not in self._domain_vcpus[domid]: 1.239 + continue 1.240 + 1.241 + prv_usage_at, prv_usage = \ 1.242 + self._domain_vcpus[domid][vcpu] 1.243 + interval_s = (usage_at - prv_usage_at) * 1000000000 1.244 + if interval_s > 0: 1.245 + util = (usage - prv_usage) / interval_s 1.246 + self._domain_vcpus_util[domid][vcpu] = util 1.247 + 1.248 + self._domain_vcpus[domid] = cputimes 1.249 + 1.250 + # Calculate utilisation for VBDs 1.251 + 1.252 + for domid, vbds in self._get_vbd_stats().items(): 1.253 + if domid not in self._domain_vbds: 1.254 + self._domain_vbds[domid] = vbds 1.255 + self._domain_vbds_util[domid] = {} 1.256 + continue 1.257 + 1.258 + for devid, (usage_at, rd, wr) in vbds.items(): 1.259 + if devid not in self._domain_vbds[domid]: 1.260 + continue 1.261 + 1.262 + prv_at, prv_rd, prv_wr = \ 1.263 + self._domain_vbds[domid][devid] 1.264 + interval = usage_at - prv_at 1.265 + rd_util = (rd - prv_rd)/interval 1.266 + wr_util = (wr - prv_wr)/interval 1.267 + self._domain_vbds_util[domid][devid] = \ 1.268 + (rd_util, wr_util) 1.269 + 1.270 + self._domain_vbds[domid] = vbds 1.271 + 1.272 + 1.273 + # Calculate utilisation for VIFs 1.274 + 1.275 + for domid, vifs in self._get_vif_stats().items(): 1.276 + 1.277 + if domid not in self._domain_vifs: 1.278 + self._domain_vifs[domid] = vifs 1.279 + self._domain_vifs_util[domid] = {} 1.280 + continue 1.281 + 1.282 + for devid, (usage_at, rx, tx) in vifs.items(): 1.283 + if devid not in self._domain_vifs[domid]: 1.284 + continue 1.285 + 1.286 + prv_at, prv_rx, prv_tx = \ 1.287 + self._domain_vifs[domid][devid] 1.288 + interval = usage_at - prv_at 1.289 + rx_util = (rx - prv_rx)/interval 1.290 + tx_util = (tx - prv_tx)/interval 1.291 + 1.292 + # note these are flipped around because 1.293 + # we are measuring the host interface, 1.294 + # not the guest interface 1.295 + self._domain_vifs_util[domid][devid] = \ 1.296 + (tx_util, rx_util) 1.297 + 1.298 + self._domain_vifs[domid] = vifs 1.299 + 1.300 + # Calculate utilisation for PIFs 1.301 + 1.302 + for pifid, stats in self._get_pif_stats().items(): 1.303 + if pifid not in self.pifs: 1.304 + self.pifs[pifid] = stats 1.305 + continue 1.306 + 1.307 + usage_at, rx, tx = stats 1.308 + prv_at, prv_rx, prv_tx = self.pifs[pifid] 1.309 + interval = usage_at - prv_at 1.310 + rx_util = (rx - prv_rx)/interval 1.311 + tx_util = (tx - prv_tx)/interval 1.312 + 1.313 + self.pifs_util[pifid] = (rx_util, tx_util) 1.314 + self.pifs[pifid] = stats 1.315 + 1.316 + finally: 1.317 + self.lock.release() 1.318 + 1.319 + # Sleep a while before next poll 1.320 + time.sleep(POLL_INTERVAL) 1.321 +