debuggers.hg

annotate tools/xenmon/xenmon.py @ 22848:6341fe0f4e5a

Added tag 4.1.0-rc2 for changeset 9dca60d88c63
author Keir Fraser <keir@xen.org>
date Tue Jan 25 14:06:55 2011 +0000 (2011-01-25)
parents 7bee812a0397
children
rev   line source
kaf24@7840 1 #!/usr/bin/env python
kaf24@7840 2
kaf24@7840 3 #####################################################################
kaf24@7840 4 # xenmon is a front-end for xenbaked.
kaf24@7840 5 # There is a curses interface for live monitoring. XenMon also allows
kaf24@7840 6 # logging to a file. For options, run python xenmon.py -h
kaf24@7840 7 #
kaf24@9685 8 # Copyright (C) 2005,2006 by Hewlett Packard, Palo Alto and Fort Collins
kaf24@7840 9 # Authors: Lucy Cherkasova, lucy.cherkasova@hp.com
kaf24@7840 10 # Rob Gardner, rob.gardner@hp.com
kaf24@7840 11 # Diwaker Gupta, diwaker.gupta@hp.com
kaf24@7840 12 #####################################################################
kaf24@7840 13 # This program is free software; you can redistribute it and/or modify
kaf24@7840 14 # it under the terms of the GNU General Public License as published by
kaf24@7840 15 # the Free Software Foundation; under version 2 of the License.
kaf24@7840 16 #
kaf24@7840 17 # This program is distributed in the hope that it will be useful,
kaf24@7840 18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
kaf24@7840 19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
kaf24@7840 20 # GNU General Public License for more details.
kaf24@7840 21 #
kaf24@7840 22 # You should have received a copy of the GNU General Public License
kaf24@7840 23 # along with this program; if not, write to the Free Software
kaf24@7840 24 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
kaf24@7840 25 #####################################################################
kaf24@7840 26
kaf24@7840 27 import mmap
kaf24@7840 28 import struct
kaf24@7840 29 import os
kaf24@7840 30 import time
kaf24@7840 31 import optparse as _o
kaf24@7840 32 import curses as _c
kaf24@7840 33 import math
kaf24@7840 34 import sys
kaf24@7840 35
kaf24@7840 36 # constants
kaf24@7840 37 NSAMPLES = 100
kaf24@7840 38 NDOMAINS = 32
kfraser@10697 39 IDLE_DOMAIN = -1 # idle domain's ID
kaf24@7840 40
kaf24@7840 41 # the struct strings for qos_info
kfraser@10697 42 ST_DOM_INFO = "6Q3i2H32s"
kaf24@7840 43 ST_QDATA = "%dQ" % (6*NDOMAINS + 4)
kaf24@7840 44
kaf24@7840 45 # size of mmaped file
kaf24@7840 46 QOS_DATA_SIZE = struct.calcsize(ST_QDATA)*NSAMPLES + struct.calcsize(ST_DOM_INFO)*NDOMAINS + struct.calcsize("4i")
kaf24@7840 47
kaf24@7840 48 # location of mmaped file, hard coded right now
keir@16195 49 SHM_FILE = "/var/run/xenq-shm"
kaf24@7840 50
kaf24@7840 51 # format strings
kaf24@7840 52 TOTALS = 15*' ' + "%6.2f%%" + 35*' ' + "%6.2f%%"
kaf24@7840 53
kaf24@7840 54 ALLOCATED = "Allocated"
kaf24@7840 55 GOTTEN = "Gotten"
kaf24@7840 56 BLOCKED = "Blocked"
kaf24@7840 57 WAITED = "Waited"
kaf24@7840 58 IOCOUNT = "I/O Count"
kaf24@7840 59 EXCOUNT = "Exec Count"
kaf24@7840 60
kaf24@7840 61 # globals
kaf24@7937 62 dom_in_use = []
kaf24@7937 63
kaf24@7840 64 # our curses screen
kaf24@7840 65 stdscr = None
kaf24@7840 66
kaf24@7840 67 # parsed options
kaf24@7840 68 options, args = None, None
kaf24@7840 69
kaf24@7840 70 # the optparse module is quite smart
kaf24@7840 71 # to see help, just run xenmon -h
kaf24@7840 72 def setup_cmdline_parser():
kaf24@7840 73 parser = _o.OptionParser()
kaf24@7840 74 parser.add_option("-l", "--live", dest="live", action="store_true",
kaf24@7840 75 default=True, help = "show the ncurses live monitoring frontend (default)")
kaf24@7840 76 parser.add_option("-n", "--notlive", dest="live", action="store_false",
kaf24@7840 77 default="True", help = "write to file instead of live monitoring")
kaf24@7840 78 parser.add_option("-p", "--prefix", dest="prefix",
kaf24@7840 79 default = "log", help="prefix to use for output files")
kaf24@7840 80 parser.add_option("-t", "--time", dest="duration",
kaf24@7840 81 action="store", type="int", default=10,
kaf24@7840 82 help="stop logging to file after this much time has elapsed (in seconds). set to 0 to keep logging indefinitely")
kaf24@7840 83 parser.add_option("-i", "--interval", dest="interval",
kaf24@7840 84 action="store", type="int", default=1000,
kaf24@7840 85 help="interval for logging (in ms)")
kaf24@7840 86 parser.add_option("--ms_per_sample", dest="mspersample",
kaf24@7840 87 action="store", type="int", default=100,
kaf24@7840 88 help = "determines how many ms worth of data goes in a sample")
kaf24@9685 89 parser.add_option("--cpu", dest="cpu", action="store", type="int", default=0,
kaf24@9685 90 help = "specifies which cpu to display data for")
kaf24@9685 91
kaf24@9685 92 parser.add_option("--allocated", dest="allocated", action="store_true",
kaf24@9685 93 default=False, help="Display allocated time for each domain")
kaf24@9685 94 parser.add_option("--noallocated", dest="allocated", action="store_false",
kaf24@9685 95 default=False, help="Don't display allocated time for each domain")
kaf24@9685 96
kaf24@9685 97 parser.add_option("--blocked", dest="blocked", action="store_true",
kaf24@9685 98 default=True, help="Display blocked time for each domain")
kaf24@9685 99 parser.add_option("--noblocked", dest="blocked", action="store_false",
kaf24@9685 100 default=True, help="Don't display blocked time for each domain")
kaf24@9685 101
kaf24@9685 102 parser.add_option("--waited", dest="waited", action="store_true",
kaf24@9685 103 default=True, help="Display waiting time for each domain")
kaf24@9685 104 parser.add_option("--nowaited", dest="waited", action="store_false",
kaf24@9685 105 default=True, help="Don't display waiting time for each domain")
kaf24@9685 106
kaf24@9685 107 parser.add_option("--excount", dest="excount", action="store_true",
kaf24@9685 108 default=False, help="Display execution count for each domain")
kaf24@9685 109 parser.add_option("--noexcount", dest="excount", action="store_false",
kaf24@9685 110 default=False, help="Don't display execution count for each domain")
kaf24@9685 111 parser.add_option("--iocount", dest="iocount", action="store_true",
kaf24@9685 112 default=False, help="Display I/O count for each domain")
kaf24@9685 113 parser.add_option("--noiocount", dest="iocount", action="store_false",
kaf24@9685 114 default=False, help="Don't display I/O count for each domain")
kaf24@9685 115
kaf24@7840 116 return parser
kaf24@7840 117
kaf24@7840 118 # encapsulate information about a domain
kaf24@7840 119 class DomainInfo:
kaf24@7840 120 def __init__(self):
kaf24@7937 121 self.allocated_sum = 0
kaf24@7937 122 self.gotten_sum = 0
kaf24@7937 123 self.blocked_sum = 0
kaf24@7937 124 self.waited_sum = 0
kaf24@7937 125 self.exec_count = 0;
kaf24@7937 126 self.iocount_sum = 0
kaf24@7840 127 self.ffp_samples = []
kaf24@7840 128
kaf24@7840 129 def gotten_stats(self, passed):
kaf24@7937 130 total = float(self.gotten_sum)
kaf24@7840 131 per = 100*total/passed
kaf24@7937 132 exs = self.exec_count
kaf24@7840 133 if exs > 0:
kaf24@7840 134 avg = total/exs
kaf24@7840 135 else:
kaf24@7840 136 avg = 0
kaf24@7840 137 return [total/(float(passed)/10**9), per, avg]
kaf24@7840 138
kaf24@7840 139 def waited_stats(self, passed):
kaf24@7937 140 total = float(self.waited_sum)
kaf24@7840 141 per = 100*total/passed
kaf24@7937 142 exs = self.exec_count
kaf24@7840 143 if exs > 0:
kaf24@7840 144 avg = total/exs
kaf24@7840 145 else:
kaf24@7840 146 avg = 0
kaf24@7840 147 return [total/(float(passed)/10**9), per, avg]
kaf24@7840 148
kaf24@7840 149 def blocked_stats(self, passed):
kaf24@7937 150 total = float(self.blocked_sum)
kaf24@7840 151 per = 100*total/passed
kaf24@7937 152 ios = self.iocount_sum
kaf24@7840 153 if ios > 0:
kaf24@7840 154 avg = total/float(ios)
kaf24@7840 155 else:
kaf24@7840 156 avg = 0
kaf24@7840 157 return [total/(float(passed)/10**9), per, avg]
kaf24@7840 158
kaf24@7840 159 def allocated_stats(self, passed):
kaf24@7937 160 total = self.allocated_sum
kaf24@7937 161 exs = self.exec_count
kaf24@7840 162 if exs > 0:
kaf24@7840 163 return float(total)/exs
kaf24@7840 164 else:
kaf24@7840 165 return 0
kaf24@7840 166
kaf24@7840 167 def ec_stats(self, passed):
kaf24@7937 168 total = float(self.exec_count/(float(passed)/10**9))
ewan@14567 169 return total
kaf24@7840 170
kaf24@7840 171 def io_stats(self, passed):
kaf24@7937 172 total = float(self.iocount_sum)
kaf24@7937 173 exs = self.exec_count
kaf24@7840 174 if exs > 0:
kaf24@7840 175 avg = total/exs
kaf24@7840 176 else:
kaf24@7840 177 avg = 0
kaf24@7840 178 return [total/(float(passed)/10**9), avg]
kaf24@7840 179
kaf24@7840 180 def stats(self, passed):
kaf24@7840 181 return [self.gotten_stats(passed), self.allocated_stats(passed), self.blocked_stats(passed),
kaf24@7840 182 self.waited_stats(passed), self.ec_stats(passed), self.io_stats(passed)]
kaf24@7840 183
kaf24@7840 184 # report values over desired interval
kaf24@7840 185 def summarize(startat, endat, duration, samples):
kaf24@7840 186 dominfos = {}
kaf24@7840 187 for i in range(0, NDOMAINS):
kaf24@7840 188 dominfos[i] = DomainInfo()
kaf24@7840 189
kaf24@7840 190 passed = 1 # to prevent zero division
kaf24@7840 191 curid = startat
kaf24@7840 192 numbuckets = 0
kaf24@7840 193 lost_samples = []
kaf24@7840 194 ffp_samples = []
kaf24@7840 195
kaf24@7840 196 while passed < duration:
kaf24@7840 197 for i in range(0, NDOMAINS):
kaf24@7937 198 if dom_in_use[i]:
kaf24@7937 199 dominfos[i].gotten_sum += samples[curid][0*NDOMAINS + i]
kaf24@7937 200 dominfos[i].allocated_sum += samples[curid][1*NDOMAINS + i]
kaf24@7937 201 dominfos[i].waited_sum += samples[curid][2*NDOMAINS + i]
kaf24@7937 202 dominfos[i].blocked_sum += samples[curid][3*NDOMAINS + i]
kaf24@7937 203 dominfos[i].exec_count += samples[curid][4*NDOMAINS + i]
kaf24@7937 204 dominfos[i].iocount_sum += samples[curid][5*NDOMAINS + i]
kaf24@7840 205
kaf24@7840 206 passed += samples[curid][6*NDOMAINS]
kaf24@7840 207 lost_samples.append(samples[curid][6*NDOMAINS + 2])
kaf24@7840 208 ffp_samples.append(samples[curid][6*NDOMAINS + 3])
kaf24@7840 209
kaf24@7840 210 numbuckets += 1
kaf24@7840 211
kaf24@7840 212 if curid > 0:
kaf24@7840 213 curid -= 1
kaf24@7840 214 else:
kaf24@7840 215 curid = NSAMPLES - 1
kaf24@7840 216 if curid == endat:
kaf24@7840 217 break
kaf24@7840 218
kaf24@7840 219 lostinfo = [min(lost_samples), sum(lost_samples), max(lost_samples)]
kaf24@7840 220 ffpinfo = [min(ffp_samples), sum(ffp_samples), max(ffp_samples)]
kaf24@7937 221
kaf24@7937 222 ldoms = []
kaf24@7937 223 for x in range(0, NDOMAINS):
kaf24@7937 224 if dom_in_use[x]:
kaf24@7937 225 ldoms.append(dominfos[x].stats(passed))
kaf24@7937 226 else:
kaf24@7937 227 ldoms.append(0)
kaf24@7840 228
kaf24@7840 229 return [ldoms, lostinfo, ffpinfo]
kaf24@7840 230
kaf24@7840 231 # scale microseconds to milliseconds or seconds as necessary
kaf24@7840 232 def time_scale(ns):
kaf24@7840 233 if ns < 1000:
kaf24@7840 234 return "%4.2f ns" % float(ns)
kaf24@7840 235 elif ns < 1000*1000:
kaf24@7840 236 return "%4.2f us" % (float(ns)/10**3)
kaf24@7840 237 elif ns < 10**9:
ewan@14567 238 return "%4.2f ms" % (float(ns)/10**6)
kaf24@7840 239 else:
kaf24@7840 240 return "%4.2f s" % (float(ns)/10**9)
kaf24@7840 241
kaf24@7840 242 # paint message on curses screen, but detect screen size errors
kaf24@7840 243 def display(scr, row, col, str, attr=0):
kaf24@7840 244 try:
kaf24@7840 245 scr.addstr(row, col, str, attr)
kaf24@7840 246 except:
kaf24@7840 247 scr.erase()
kaf24@7840 248 _c.nocbreak()
kaf24@7840 249 scr.keypad(0)
kaf24@7840 250 _c.echo()
kaf24@7840 251 _c.endwin()
kaf24@7840 252 print "Your terminal screen is not big enough; Please resize it."
kaf24@7840 253 print "row=%d, col=%d, str='%s'" % (row, col, str)
kaf24@7840 254 sys.exit(1)
kaf24@7840 255
kaf24@7840 256
kfraser@10678 257 # diplay domain id
kfraser@10678 258 def display_domain_id(scr, row, col, dom):
kfraser@10678 259 if dom == IDLE_DOMAIN:
kfraser@10678 260 display(scr, row, col-1, "Idle")
kfraser@10678 261 else:
kfraser@10678 262 display(scr, row, col, "%d" % dom)
kfraser@10678 263
kfraser@10678 264
kaf24@7840 265 # the live monitoring code
kaf24@9685 266 def show_livestats(cpu):
kaf24@7840 267 ncpu = 1 # number of cpu's on this platform
kaf24@7840 268 slen = 0 # size of shared data structure, incuding padding
kaf24@9685 269 cpu_1sec_usage = 0.0
kaf24@9685 270 cpu_10sec_usage = 0.0
kaf24@9685 271 heartbeat = 1
kaf24@9685 272 global dom_in_use, options
kaf24@7840 273
kaf24@7840 274 # mmap the (the first chunk of the) file
kaf24@7840 275 shmf = open(SHM_FILE, "r+")
kaf24@7840 276 shm = mmap.mmap(shmf.fileno(), QOS_DATA_SIZE)
kaf24@7840 277
kaf24@7840 278 # initialize curses
kaf24@7840 279 stdscr = _c.initscr()
kaf24@7840 280 _c.noecho()
kaf24@7840 281 _c.cbreak()
kaf24@7840 282
kaf24@7840 283 stdscr.keypad(1)
kaf24@7840 284 stdscr.timeout(1000)
kaf24@7840 285 [maxy, maxx] = stdscr.getmaxyx()
kaf24@7840 286
kaf24@7840 287 # display in a loop
kaf24@7840 288 while True:
kaf24@7840 289
kaf24@9685 290 cpuidx = 0
kaf24@9685 291 while cpuidx < ncpu:
kaf24@7840 292
kaf24@7840 293 # calculate offset in mmap file to start from
kaf24@7840 294 idx = cpuidx * slen
kaf24@7840 295
kaf24@7840 296
kaf24@7840 297 samples = []
kaf24@7840 298 doms = []
kaf24@9685 299 dom_in_use = []
kfraser@10697 300 domain_id = []
kaf24@7840 301
kaf24@7840 302 # read in data
kaf24@7840 303 for i in range(0, NSAMPLES):
kaf24@7840 304 len = struct.calcsize(ST_QDATA)
kaf24@7840 305 sample = struct.unpack(ST_QDATA, shm[idx:idx+len])
kaf24@7840 306 samples.append(sample)
kaf24@7840 307 idx += len
kaf24@7840 308
kaf24@7840 309 for i in range(0, NDOMAINS):
kaf24@7840 310 len = struct.calcsize(ST_DOM_INFO)
kaf24@7840 311 dom = struct.unpack(ST_DOM_INFO, shm[idx:idx+len])
kaf24@7840 312 doms.append(dom)
keir@16116 313 # (last_update_time, start_time, runnable_start_time, blocked_start_time,
keir@16116 314 # ns_since_boot, ns_oncpu_since_boot, runnable_at_last_update,
keir@16116 315 # runnable, in_use, domid, junk, name) = dom
keir@16116 316 # dom_in_use.append(in_use)
kaf24@7937 317 dom_in_use.append(dom[8])
kfraser@10697 318 domid = dom[9]
kfraser@10697 319 if domid == 32767 :
kfraser@10697 320 domid = IDLE_DOMAIN
kfraser@10697 321 domain_id.append(domid)
kaf24@7840 322 idx += len
kaf24@9685 323 # print "dom_in_use(cpu=%d): " % cpuidx, dom_in_use
kaf24@9685 324
kaf24@7840 325
kaf24@7840 326 len = struct.calcsize("4i")
kaf24@7840 327 oldncpu = ncpu
kaf24@7840 328 (next, ncpu, slen, freq) = struct.unpack("4i", shm[idx:idx+len])
kaf24@7840 329 idx += len
kaf24@7840 330
kaf24@7840 331 # xenbaked tells us how many cpu's it's got, so re-do
kaf24@7840 332 # the mmap if necessary to get multiple cpu data
kaf24@7840 333 if oldncpu != ncpu:
kaf24@7840 334 shm = mmap.mmap(shmf.fileno(), ncpu*slen)
kaf24@7840 335
kaf24@7840 336 # if we've just calculated data for the cpu of interest, then
kaf24@7840 337 # stop examining mmap data and start displaying stuff
kaf24@7840 338 if cpuidx == cpu:
kaf24@7840 339 break
kaf24@7840 340
kaf24@9685 341 cpuidx = cpuidx + 1
kaf24@9685 342
kaf24@7840 343 # calculate starting and ending datapoints; never look at "next" since
kaf24@7840 344 # it represents live data that may be in transition.
kaf24@7840 345 startat = next - 1
kaf24@7840 346 if next + 10 < NSAMPLES:
kaf24@7840 347 endat = next + 10
kaf24@7840 348 else:
kaf24@7840 349 endat = 10
kaf24@7840 350
kaf24@7840 351 # get summary over desired interval
kaf24@7840 352 [h1, l1, f1] = summarize(startat, endat, 10**9, samples)
kaf24@7840 353 [h2, l2, f2] = summarize(startat, endat, 10 * 10**9, samples)
kaf24@7840 354
kaf24@7937 355
kaf24@7840 356 # the actual display code
kaf24@7840 357 row = 0
kaf24@7840 358 display(stdscr, row, 1, "CPU = %d" % cpu, _c.A_STANDOUT)
kaf24@7840 359
kaf24@9685 360 display(stdscr, row, 10, "%sLast 10 seconds (%3.2f%%)%sLast 1 second (%3.2f%%)" % (6*' ', cpu_10sec_usage, 30*' ', cpu_1sec_usage), _c.A_BOLD)
kaf24@7840 361 row +=1
kaf24@7840 362 display(stdscr, row, 1, "%s" % ((maxx-2)*'='))
kaf24@7840 363
kaf24@7840 364 total_h1_cpu = 0
kaf24@7840 365 total_h2_cpu = 0
kaf24@7840 366
kaf24@9685 367 cpu_1sec_usage = 0.0
kaf24@9685 368 cpu_10sec_usage = 0.0
kaf24@9685 369
kaf24@7840 370 for dom in range(0, NDOMAINS):
kaf24@7937 371 if not dom_in_use[dom]:
kaf24@7937 372 continue
kaf24@7937 373
kfraser@10697 374 if h1[dom][0][1] > 0 or domain_id[dom] == IDLE_DOMAIN:
kaf24@7840 375 # display gotten
kaf24@7840 376 row += 1
kaf24@7840 377 col = 2
kfraser@10697 378 display_domain_id(stdscr, row, col, domain_id[dom])
kaf24@7840 379 col += 4
kaf24@7840 380 display(stdscr, row, col, "%s" % time_scale(h2[dom][0][0]))
kaf24@7840 381 col += 12
kaf24@7840 382 display(stdscr, row, col, "%3.2f%%" % h2[dom][0][1])
kfraser@10697 383 if dom != IDLE_DOMAIN:
kaf24@9685 384 cpu_10sec_usage += h2[dom][0][1]
kaf24@7840 385 col += 12
kaf24@7840 386 display(stdscr, row, col, "%s/ex" % time_scale(h2[dom][0][2]))
kaf24@7840 387 col += 18
kaf24@7840 388 display(stdscr, row, col, "%s" % time_scale(h1[dom][0][0]))
kaf24@7840 389 col += 12
kaf24@9685 390 display(stdscr, row, col, "%3.2f%%" % h1[dom][0][1], _c.A_STANDOUT)
kaf24@7840 391 col += 12
kaf24@7840 392 display(stdscr, row, col, "%s/ex" % time_scale(h1[dom][0][2]))
kaf24@7840 393 col += 18
kaf24@7840 394 display(stdscr, row, col, "Gotten")
kaf24@9685 395
kfraser@10697 396 if dom != IDLE_DOMAIN:
kaf24@9685 397 cpu_1sec_usage = cpu_1sec_usage + h1[dom][0][1]
kaf24@7840 398
kaf24@7840 399 # display allocated
kaf24@9685 400 if options.allocated:
kaf24@9685 401 row += 1
kaf24@9685 402 col = 2
kfraser@10697 403 display_domain_id(stdscr, row, col, domain_id[dom])
kaf24@9685 404 col += 28
kaf24@9685 405 display(stdscr, row, col, "%s/ex" % time_scale(h2[dom][1]))
kaf24@9685 406 col += 42
kaf24@9685 407 display(stdscr, row, col, "%s/ex" % time_scale(h1[dom][1]))
kaf24@9685 408 col += 18
kaf24@9685 409 display(stdscr, row, col, "Allocated")
kaf24@7840 410
kaf24@7840 411 # display blocked
kaf24@9685 412 if options.blocked:
kaf24@9685 413 row += 1
kaf24@9685 414 col = 2
kfraser@10697 415 display_domain_id(stdscr, row, col, domain_id[dom])
kaf24@9685 416 col += 4
kaf24@9685 417 display(stdscr, row, col, "%s" % time_scale(h2[dom][2][0]))
kaf24@9685 418 col += 12
kaf24@9685 419 display(stdscr, row, col, "%3.2f%%" % h2[dom][2][1])
kaf24@9685 420 col += 12
kaf24@9685 421 display(stdscr, row, col, "%s/io" % time_scale(h2[dom][2][2]))
kaf24@9685 422 col += 18
kaf24@9685 423 display(stdscr, row, col, "%s" % time_scale(h1[dom][2][0]))
kaf24@9685 424 col += 12
kaf24@9685 425 display(stdscr, row, col, "%3.2f%%" % h1[dom][2][1])
kaf24@9685 426 col += 12
kaf24@9685 427 display(stdscr, row, col, "%s/io" % time_scale(h1[dom][2][2]))
kaf24@9685 428 col += 18
kaf24@9685 429 display(stdscr, row, col, "Blocked")
kaf24@7840 430
kaf24@7840 431 # display waited
kaf24@9685 432 if options.waited:
kaf24@9685 433 row += 1
kaf24@9685 434 col = 2
kfraser@10697 435 display_domain_id(stdscr, row, col, domain_id[dom])
kaf24@9685 436 col += 4
kaf24@9685 437 display(stdscr, row, col, "%s" % time_scale(h2[dom][3][0]))
kaf24@9685 438 col += 12
kaf24@9685 439 display(stdscr, row, col, "%3.2f%%" % h2[dom][3][1])
kaf24@9685 440 col += 12
kaf24@9685 441 display(stdscr, row, col, "%s/ex" % time_scale(h2[dom][3][2]))
kaf24@9685 442 col += 18
kaf24@9685 443 display(stdscr, row, col, "%s" % time_scale(h1[dom][3][0]))
kaf24@9685 444 col += 12
kaf24@9685 445 display(stdscr, row, col, "%3.2f%%" % h1[dom][3][1])
kaf24@9685 446 col += 12
kaf24@9685 447 display(stdscr, row, col, "%s/ex" % time_scale(h1[dom][3][2]))
kaf24@9685 448 col += 18
kaf24@9685 449 display(stdscr, row, col, "Waited")
kaf24@7840 450
kaf24@7840 451 # display ex count
kaf24@9685 452 if options.excount:
kaf24@9685 453 row += 1
kaf24@9685 454 col = 2
kfraser@10697 455 display_domain_id(stdscr, row, col, domain_id[dom])
kaf24@9685 456
kaf24@9685 457 col += 28
kaf24@9685 458 display(stdscr, row, col, "%d/s" % h2[dom][4])
kaf24@9685 459 col += 42
kaf24@9685 460 display(stdscr, row, col, "%d" % h1[dom][4])
kaf24@9685 461 col += 18
kaf24@9685 462 display(stdscr, row, col, "Execution count")
kaf24@7840 463
kaf24@7840 464 # display io count
kaf24@9685 465 if options.iocount:
kaf24@9685 466 row += 1
kaf24@9685 467 col = 2
kfraser@10697 468 display_domain_id(stdscr, row, col, domain_id[dom])
kaf24@9685 469 col += 4
kaf24@9685 470 display(stdscr, row, col, "%d/s" % h2[dom][5][0])
kaf24@9685 471 col += 24
kaf24@9685 472 display(stdscr, row, col, "%d/ex" % h2[dom][5][1])
kaf24@9685 473 col += 18
kaf24@9685 474 display(stdscr, row, col, "%d" % h1[dom][5][0])
kaf24@9685 475 col += 24
kaf24@9685 476 display(stdscr, row, col, "%3.2f/ex" % h1[dom][5][1])
kaf24@9685 477 col += 18
kaf24@9685 478 display(stdscr, row, col, "I/O Count")
kaf24@7840 479
kaf24@7840 480 #row += 1
kaf24@7840 481 #stdscr.hline(row, 1, '-', maxx - 2)
kaf24@7840 482 total_h1_cpu += h1[dom][0][1]
kaf24@7840 483 total_h2_cpu += h2[dom][0][1]
kaf24@7840 484
kaf24@7840 485
kaf24@7840 486 row += 1
kaf24@9685 487 star = heartbeat * '*'
kaf24@9685 488 heartbeat = 1 - heartbeat
kaf24@9685 489 display(stdscr, row, 1, star)
kaf24@7840 490 display(stdscr, row, 2, TOTALS % (total_h2_cpu, total_h1_cpu))
kaf24@7840 491 row += 1
kaf24@7840 492 # display(stdscr, row, 2,
kaf24@7840 493 # "\tFFP: %d (Min: %d, Max: %d)\t\t\tFFP: %d (Min: %d, Max %d)" %
kaf24@7840 494 # (math.ceil(f2[1]), f2[0], f2[2], math.ceil(f1[1]), f1[0], f1[2]), _c.A_BOLD)
kaf24@7840 495
kaf24@7840 496 if l1[1] > 1 :
kaf24@7840 497 row += 1
kaf24@7840 498 display(stdscr, row, 2,
kaf24@7840 499 "\tRecords lost: %d (Min: %d, Max: %d)\t\t\tRecords lost: %d (Min: %d, Max %d)" %
kaf24@7840 500 (math.ceil(l2[1]), l2[0], l2[2], math.ceil(l1[1]), l1[0], l1[2]), _c.A_BOLD)
kaf24@7840 501
kaf24@7840 502 # grab a char from tty input; exit if interrupt hit
kaf24@7840 503 try:
kaf24@7840 504 c = stdscr.getch()
kaf24@7840 505 except:
kaf24@7840 506 break
kaf24@7840 507
kaf24@7840 508 # q = quit
kaf24@7840 509 if c == ord('q'):
kaf24@7840 510 break
kaf24@7840 511
kaf24@7840 512 # c = cycle to a new cpu of interest
kaf24@7840 513 if c == ord('c'):
kaf24@7840 514 cpu = (cpu + 1) % ncpu
kaf24@7840 515
kaf24@9192 516 # n/p = cycle to the next/previous CPU
kaf24@9192 517 if c == ord('n'):
kaf24@9192 518 cpu = (cpu + 1) % ncpu
kaf24@9192 519 if c == ord('p'):
kaf24@9192 520 cpu = (cpu - 1) % ncpu
kaf24@9192 521
kaf24@7840 522 stdscr.erase()
kaf24@7840 523
kaf24@7840 524 _c.nocbreak()
kaf24@7840 525 stdscr.keypad(0)
kaf24@7840 526 _c.echo()
kaf24@7840 527 _c.endwin()
kaf24@7840 528 shm.close()
kaf24@7840 529 shmf.close()
kaf24@7840 530
kaf24@7840 531
kaf24@7840 532 # simple functions to allow initialization of log files without actually
kaf24@7840 533 # physically creating files that are never used; only on the first real
kaf24@7840 534 # write does the file get created
kaf24@7840 535 class Delayed(file):
kaf24@7840 536 def __init__(self, filename, mode):
ewan@14567 537 self.filename = filename
ewan@14567 538 self.saved_mode = mode
ewan@14567 539 self.delay_data = ""
ewan@14567 540 self.opened = 0
kaf24@7840 541
kaf24@7840 542 def delayed_write(self, str):
ewan@14567 543 self.delay_data = str
kaf24@7840 544
kaf24@7840 545 def write(self, str):
ewan@14567 546 if not self.opened:
ewan@14567 547 self.file = open(self.filename, self.saved_mode)
ewan@14567 548 self.opened = 1
kaf24@7840 549 self.file.write(self.delay_data)
ewan@14567 550 self.file.write(str)
kaf24@7840 551
kfraser@10697 552 def rename(self, name):
kfraser@10697 553 self.filename = name
kfraser@10697 554
kaf24@7840 555 def flush(self):
kaf24@7840 556 if self.opened:
kaf24@7840 557 self.file.flush()
kaf24@7840 558
kaf24@7840 559 def close(self):
kaf24@7840 560 if self.opened:
kaf24@7840 561 self.file.close()
kaf24@7840 562
kaf24@7840 563
kaf24@7840 564 def writelog():
kaf24@7840 565 global options
kaf24@7937 566 global dom_in_use
kaf24@7840 567
kaf24@7840 568 ncpu = 1 # number of cpu's
kaf24@7840 569 slen = 0 # size of shared structure inc. padding
kaf24@7840 570
kaf24@7840 571 shmf = open(SHM_FILE, "r+")
kaf24@7840 572 shm = mmap.mmap(shmf.fileno(), QOS_DATA_SIZE)
kaf24@7840 573
kaf24@7840 574 interval = 0
kaf24@9191 575 curr = last = time.time()
kaf24@7840 576 outfiles = {}
kaf24@7840 577 for dom in range(0, NDOMAINS):
kfraser@10697 578 outfiles[dom] = Delayed("%s-dom%d.log" % (options.prefix, dom), 'w')
kaf24@7840 579 outfiles[dom].delayed_write("# passed cpu dom cpu(tot) cpu(%) cpu/ex allocated/ex blocked(tot) blocked(%) blocked/io waited(tot) waited(%) waited/ex ex/s io(tot) io/ex\n")
kaf24@7840 580
kaf24@7840 581 while options.duration == 0 or interval < (options.duration * 1000):
kaf24@9685 582 cpuidx = 0
kaf24@9685 583 while cpuidx < ncpu:
kaf24@7937 584
kaf24@7840 585 idx = cpuidx * slen # offset needed in mmap file
kaf24@7840 586
kaf24@7840 587 samples = []
kaf24@7840 588 doms = []
kaf24@7937 589 dom_in_use = []
kfraser@10697 590 domain_id = []
kaf24@7840 591
kaf24@7840 592 for i in range(0, NSAMPLES):
kaf24@7840 593 len = struct.calcsize(ST_QDATA)
kaf24@7840 594 sample = struct.unpack(ST_QDATA, shm[idx:idx+len])
kaf24@7840 595 samples.append(sample)
kaf24@7840 596 idx += len
kaf24@7840 597
kaf24@7840 598 for i in range(0, NDOMAINS):
kaf24@7840 599 len = struct.calcsize(ST_DOM_INFO)
kaf24@7840 600 dom = struct.unpack(ST_DOM_INFO, shm[idx:idx+len])
kaf24@7937 601 # doms.append(dom)
keir@16116 602 # (last_update_time, start_time, runnable_start_time, blocked_start_time,
keir@16116 603 # ns_since_boot, ns_oncpu_since_boot, runnable_at_last_update,
keir@16116 604 # runnable, in_use, domid, junk, name) = dom
kaf24@7937 605 dom_in_use.append(dom[8])
kfraser@10697 606 domid = dom[9]
kfraser@10697 607 if domid == 32767:
kfraser@10697 608 domid = IDLE_DOMAIN
kfraser@10697 609 domain_id.append(domid)
kfraser@10697 610 if domid == IDLE_DOMAIN:
kfraser@10697 611 outfiles[i].rename("%s-idle.log" % options.prefix)
kfraser@10697 612 else:
kfraser@10697 613 outfiles[i].rename("%s-dom%d.log" % (options.prefix, domid))
kaf24@7840 614 idx += len
kaf24@7840 615
kaf24@7840 616 len = struct.calcsize("4i")
kaf24@7840 617 oldncpu = ncpu
kaf24@7840 618 (next, ncpu, slen, freq) = struct.unpack("4i", shm[idx:idx+len])
kaf24@7840 619 idx += len
kaf24@7840 620
kaf24@7840 621 if oldncpu != ncpu:
kaf24@7840 622 shm = mmap.mmap(shmf.fileno(), ncpu*slen)
kaf24@7840 623
kaf24@7840 624 startat = next - 1
kaf24@7840 625 if next + 10 < NSAMPLES:
kaf24@7840 626 endat = next + 10
kaf24@7840 627 else:
kaf24@7840 628 endat = 10
kaf24@7840 629
kaf24@7840 630 [h1,l1, f1] = summarize(startat, endat, options.interval * 10**6, samples)
kaf24@7840 631 for dom in range(0, NDOMAINS):
kaf24@7937 632 if not dom_in_use[dom]:
kaf24@7937 633 continue
kfraser@10697 634 if h1[dom][0][1] > 0 or dom == IDLE_DOMAIN:
kaf24@7840 635 outfiles[dom].write("%.3f %d %d %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f\n" %
kfraser@10697 636 (interval, cpuidx, domain_id[dom],
kaf24@7840 637 h1[dom][0][0], h1[dom][0][1], h1[dom][0][2],
kaf24@7840 638 h1[dom][1],
kaf24@7840 639 h1[dom][2][0], h1[dom][2][1], h1[dom][2][2],
kaf24@7840 640 h1[dom][3][0], h1[dom][3][1], h1[dom][3][2],
kaf24@7840 641 h1[dom][4],
kaf24@7840 642 h1[dom][5][0], h1[dom][5][1]))
kaf24@7840 643 outfiles[dom].flush()
kaf24@9191 644 curr = time.time()
kaf24@9191 645 interval += (curr - last) * 1000
kaf24@9191 646 last = curr
kaf24@9685 647 cpuidx = cpuidx + 1
kaf24@9191 648 time.sleep(options.interval / 1000.0)
kaf24@7840 649
kaf24@7840 650 for dom in range(0, NDOMAINS):
kaf24@7840 651 outfiles[dom].close()
kaf24@7840 652
kaf24@7840 653 # start xenbaked
kaf24@7840 654 def start_xenbaked():
kaf24@7840 655 global options
keir@16538 656 global kill_cmd
keir@16538 657 global xenbaked_cmd
keir@16538 658
keir@16538 659 os.system(kill_cmd)
keir@16538 660 os.system(xenbaked_cmd + " --ms_per_sample=%d &" %
kaf24@7840 661 options.mspersample)
kaf24@7840 662 time.sleep(1)
kaf24@7840 663
kaf24@7840 664 # stop xenbaked
kaf24@7840 665 def stop_xenbaked():
keir@16538 666 global stop_cmd
keir@16538 667 os.system(stop_cmd)
kaf24@7840 668
kaf24@7840 669 def main():
kaf24@7840 670 global options
kaf24@7840 671 global args
kaf24@7840 672 global domains
keir@16538 673 global stop_cmd
keir@16538 674 global kill_cmd
keir@16538 675 global xenbaked_cmd
keir@16538 676
keir@16538 677 if os.uname()[0] == "SunOS":
keir@16538 678 xenbaked_cmd = "/usr/lib/xenbaked"
keir@16538 679 stop_cmd = "/usr/bin/pkill -INT -z global xenbaked"
keir@16538 680 kill_cmd = "/usr/bin/pkill -KILL -z global xenbaked"
keir@16538 681 else:
keir@16538 682 # assumes that xenbaked is in your path
keir@16538 683 xenbaked_cmd = "xenbaked"
keir@16538 684 stop_cmd = "/usr/bin/pkill -INT xenbaked"
keir@16538 685 kill_cmd = "/usr/bin/pkill -KILL xenbaked"
kaf24@7840 686
kaf24@7840 687 parser = setup_cmdline_parser()
kaf24@7840 688 (options, args) = parser.parse_args()
ewan@12257 689
ewan@12257 690 if len(args):
ewan@12257 691 parser.error("No parameter required")
ewan@11463 692 if options.mspersample < 0:
ewan@11463 693 parser.error("option --ms_per_sample: invalid negative value: '%d'" %
ewan@11463 694 options.mspersample)
kfraser@11571 695 # If --ms_per_sample= is too large, no data may be logged.
kfraser@11571 696 if not options.live and options.duration != 0 and \
kfraser@11571 697 options.mspersample > options.duration * 1000:
kfraser@11571 698 parser.error("option --ms_per_sample: too large (> %d ms)" %
kfraser@11571 699 (options.duration * 1000))
kaf24@7840 700
kaf24@7840 701 start_xenbaked()
kaf24@7840 702 if options.live:
kaf24@9685 703 show_livestats(options.cpu)
kaf24@7840 704 else:
kaf24@7840 705 try:
kaf24@7840 706 writelog()
kaf24@7840 707 except:
kaf24@7840 708 print 'Quitting.'
kaf24@7840 709 stop_xenbaked()
kaf24@7840 710
kaf24@7840 711 if __name__ == "__main__":
kaf24@7840 712 main()