mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 18:21:39 +01:00
As recommended in http://www.gnu.org/licenses/gpl-howto.en.html. This is the result of: $ git grep -El Mass\|Temple\|Franklin | xargs ./fsf.pl Where fsf.pl is: #!/usr/bin/perl -w -pi.bak -0777 my $repl = 'If not, see <http://www.gnu.org/licenses/>.'; my $br = qr/(?:\s*\n\s*(?:[\*\#]|\/\/|\.\\" )?\s*|\s+)/; my $inwt = qr/[Ii]f${br}not,${br}write${br}(?:to${br})?the${br}Free${br}Software${br}Foundation,(?:${br}Inc\.,)?/; my $mass = qr/675${br}Mass${br}Ave,?${br}Cambridge,?${br}MA${br}02139,?${br}USA,?\.?/; my $franklin = qr/51${br}Franklin${br}St(?:reet)?(?:,${br}| - )Fifth${br}Floor,?${br}Boston,?${br}MA,?${br}02110-1301,?${br}USA,?\.?/; my $temple = qr/59${br}Temple${br}Place(?:,${br}| - )Suite${br}330,?${br}Boston,?${br}MA,?${br}021110?-1307,?${br}USA,?\.?/; s|$inwt$br$mass|$repl|m; s|$inwt$br$franklin|$repl|m; s|$inwt$br$temple|$repl|m; The only remaining mentions of these addresses are in COPYING files which I haven't touched. Some of the changed files are imports from elsewhere, however filtering them out is tricky, I think it is tolerable to have these files be modified here and then perhaps reverted on the next sync, since it's only 1-2 lines and obvious what is going on. Signed-off-by: Ian Campbell <ian.campbell@citrix.com>
712 lines
24 KiB
Python
712 lines
24 KiB
Python
#!/usr/bin/env python
|
|
|
|
#####################################################################
|
|
# xenmon is a front-end for xenbaked.
|
|
# There is a curses interface for live monitoring. XenMon also allows
|
|
# logging to a file. For options, run python xenmon.py -h
|
|
#
|
|
# Copyright (C) 2005,2006 by Hewlett Packard, Palo Alto and Fort Collins
|
|
# Authors: Lucy Cherkasova, lucy.cherkasova@hp.com
|
|
# Rob Gardner, rob.gardner@hp.com
|
|
# Diwaker Gupta, diwaker.gupta@hp.com
|
|
#####################################################################
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; under version 2 of the License.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; If not, see <http://www.gnu.org/licenses/>.
|
|
#####################################################################
|
|
|
|
import mmap
|
|
import struct
|
|
import os
|
|
import time
|
|
import optparse as _o
|
|
import curses as _c
|
|
import math
|
|
import sys
|
|
|
|
# constants
|
|
NSAMPLES = 100
|
|
NDOMAINS = 32
|
|
IDLE_DOMAIN = -1 # idle domain's ID
|
|
|
|
# the struct strings for qos_info
|
|
ST_DOM_INFO = "6Q3i2H32s"
|
|
ST_QDATA = "%dQ" % (6*NDOMAINS + 4)
|
|
|
|
# size of mmaped file
|
|
QOS_DATA_SIZE = struct.calcsize(ST_QDATA)*NSAMPLES + struct.calcsize(ST_DOM_INFO)*NDOMAINS + struct.calcsize("4i")
|
|
|
|
# location of mmaped file, hard coded right now
|
|
SHM_FILE = "/var/run/xenq-shm"
|
|
|
|
# format strings
|
|
TOTALS = 15*' ' + "%6.2f%%" + 35*' ' + "%6.2f%%"
|
|
|
|
ALLOCATED = "Allocated"
|
|
GOTTEN = "Gotten"
|
|
BLOCKED = "Blocked"
|
|
WAITED = "Waited"
|
|
IOCOUNT = "I/O Count"
|
|
EXCOUNT = "Exec Count"
|
|
|
|
# globals
|
|
dom_in_use = []
|
|
|
|
# our curses screen
|
|
stdscr = None
|
|
|
|
# parsed options
|
|
options, args = None, None
|
|
|
|
# the optparse module is quite smart
|
|
# to see help, just run xenmon -h
|
|
def setup_cmdline_parser():
|
|
parser = _o.OptionParser()
|
|
parser.add_option("-l", "--live", dest="live", action="store_true",
|
|
default=True, help = "show the ncurses live monitoring frontend (default)")
|
|
parser.add_option("-n", "--notlive", dest="live", action="store_false",
|
|
default="True", help = "write to file instead of live monitoring")
|
|
parser.add_option("-p", "--prefix", dest="prefix",
|
|
default = "log", help="prefix to use for output files")
|
|
parser.add_option("-t", "--time", dest="duration",
|
|
action="store", type="int", default=10,
|
|
help="stop logging to file after this much time has elapsed (in seconds). set to 0 to keep logging indefinitely")
|
|
parser.add_option("-i", "--interval", dest="interval",
|
|
action="store", type="int", default=1000,
|
|
help="interval for logging (in ms)")
|
|
parser.add_option("--ms_per_sample", dest="mspersample",
|
|
action="store", type="int", default=100,
|
|
help = "determines how many ms worth of data goes in a sample")
|
|
parser.add_option("--cpu", dest="cpu", action="store", type="int", default=0,
|
|
help = "specifies which cpu to display data for")
|
|
|
|
parser.add_option("--allocated", dest="allocated", action="store_true",
|
|
default=False, help="Display allocated time for each domain")
|
|
parser.add_option("--noallocated", dest="allocated", action="store_false",
|
|
default=False, help="Don't display allocated time for each domain")
|
|
|
|
parser.add_option("--blocked", dest="blocked", action="store_true",
|
|
default=True, help="Display blocked time for each domain")
|
|
parser.add_option("--noblocked", dest="blocked", action="store_false",
|
|
default=True, help="Don't display blocked time for each domain")
|
|
|
|
parser.add_option("--waited", dest="waited", action="store_true",
|
|
default=True, help="Display waiting time for each domain")
|
|
parser.add_option("--nowaited", dest="waited", action="store_false",
|
|
default=True, help="Don't display waiting time for each domain")
|
|
|
|
parser.add_option("--excount", dest="excount", action="store_true",
|
|
default=False, help="Display execution count for each domain")
|
|
parser.add_option("--noexcount", dest="excount", action="store_false",
|
|
default=False, help="Don't display execution count for each domain")
|
|
parser.add_option("--iocount", dest="iocount", action="store_true",
|
|
default=False, help="Display I/O count for each domain")
|
|
parser.add_option("--noiocount", dest="iocount", action="store_false",
|
|
default=False, help="Don't display I/O count for each domain")
|
|
|
|
return parser
|
|
|
|
# encapsulate information about a domain
|
|
class DomainInfo:
|
|
def __init__(self):
|
|
self.allocated_sum = 0
|
|
self.gotten_sum = 0
|
|
self.blocked_sum = 0
|
|
self.waited_sum = 0
|
|
self.exec_count = 0;
|
|
self.iocount_sum = 0
|
|
self.ffp_samples = []
|
|
|
|
def gotten_stats(self, passed):
|
|
total = float(self.gotten_sum)
|
|
per = 100*total/passed
|
|
exs = self.exec_count
|
|
if exs > 0:
|
|
avg = total/exs
|
|
else:
|
|
avg = 0
|
|
return [total/(float(passed)/10**9), per, avg]
|
|
|
|
def waited_stats(self, passed):
|
|
total = float(self.waited_sum)
|
|
per = 100*total/passed
|
|
exs = self.exec_count
|
|
if exs > 0:
|
|
avg = total/exs
|
|
else:
|
|
avg = 0
|
|
return [total/(float(passed)/10**9), per, avg]
|
|
|
|
def blocked_stats(self, passed):
|
|
total = float(self.blocked_sum)
|
|
per = 100*total/passed
|
|
ios = self.iocount_sum
|
|
if ios > 0:
|
|
avg = total/float(ios)
|
|
else:
|
|
avg = 0
|
|
return [total/(float(passed)/10**9), per, avg]
|
|
|
|
def allocated_stats(self, passed):
|
|
total = self.allocated_sum
|
|
exs = self.exec_count
|
|
if exs > 0:
|
|
return float(total)/exs
|
|
else:
|
|
return 0
|
|
|
|
def ec_stats(self, passed):
|
|
total = float(self.exec_count/(float(passed)/10**9))
|
|
return total
|
|
|
|
def io_stats(self, passed):
|
|
total = float(self.iocount_sum)
|
|
exs = self.exec_count
|
|
if exs > 0:
|
|
avg = total/exs
|
|
else:
|
|
avg = 0
|
|
return [total/(float(passed)/10**9), avg]
|
|
|
|
def stats(self, passed):
|
|
return [self.gotten_stats(passed), self.allocated_stats(passed), self.blocked_stats(passed),
|
|
self.waited_stats(passed), self.ec_stats(passed), self.io_stats(passed)]
|
|
|
|
# report values over desired interval
|
|
def summarize(startat, endat, duration, samples):
|
|
dominfos = {}
|
|
for i in range(0, NDOMAINS):
|
|
dominfos[i] = DomainInfo()
|
|
|
|
passed = 1 # to prevent zero division
|
|
curid = startat
|
|
numbuckets = 0
|
|
lost_samples = []
|
|
ffp_samples = []
|
|
|
|
while passed < duration:
|
|
for i in range(0, NDOMAINS):
|
|
if dom_in_use[i]:
|
|
dominfos[i].gotten_sum += samples[curid][0*NDOMAINS + i]
|
|
dominfos[i].allocated_sum += samples[curid][1*NDOMAINS + i]
|
|
dominfos[i].waited_sum += samples[curid][2*NDOMAINS + i]
|
|
dominfos[i].blocked_sum += samples[curid][3*NDOMAINS + i]
|
|
dominfos[i].exec_count += samples[curid][4*NDOMAINS + i]
|
|
dominfos[i].iocount_sum += samples[curid][5*NDOMAINS + i]
|
|
|
|
passed += samples[curid][6*NDOMAINS]
|
|
lost_samples.append(samples[curid][6*NDOMAINS + 2])
|
|
ffp_samples.append(samples[curid][6*NDOMAINS + 3])
|
|
|
|
numbuckets += 1
|
|
|
|
if curid > 0:
|
|
curid -= 1
|
|
else:
|
|
curid = NSAMPLES - 1
|
|
if curid == endat:
|
|
break
|
|
|
|
lostinfo = [min(lost_samples), sum(lost_samples), max(lost_samples)]
|
|
ffpinfo = [min(ffp_samples), sum(ffp_samples), max(ffp_samples)]
|
|
|
|
ldoms = []
|
|
for x in range(0, NDOMAINS):
|
|
if dom_in_use[x]:
|
|
ldoms.append(dominfos[x].stats(passed))
|
|
else:
|
|
ldoms.append(0)
|
|
|
|
return [ldoms, lostinfo, ffpinfo]
|
|
|
|
# scale microseconds to milliseconds or seconds as necessary
|
|
def time_scale(ns):
|
|
if ns < 1000:
|
|
return "%4.2f ns" % float(ns)
|
|
elif ns < 1000*1000:
|
|
return "%4.2f us" % (float(ns)/10**3)
|
|
elif ns < 10**9:
|
|
return "%4.2f ms" % (float(ns)/10**6)
|
|
else:
|
|
return "%4.2f s" % (float(ns)/10**9)
|
|
|
|
# paint message on curses screen, but detect screen size errors
|
|
def display(scr, row, col, str, attr=0):
|
|
try:
|
|
scr.addstr(row, col, str, attr)
|
|
except:
|
|
scr.erase()
|
|
_c.nocbreak()
|
|
scr.keypad(0)
|
|
_c.echo()
|
|
_c.endwin()
|
|
print "Your terminal screen is not big enough; Please resize it."
|
|
print "row=%d, col=%d, str='%s'" % (row, col, str)
|
|
sys.exit(1)
|
|
|
|
|
|
# diplay domain id
|
|
def display_domain_id(scr, row, col, dom):
|
|
if dom == IDLE_DOMAIN:
|
|
display(scr, row, col-1, "Idle")
|
|
else:
|
|
display(scr, row, col, "%d" % dom)
|
|
|
|
|
|
# the live monitoring code
|
|
def show_livestats(cpu):
|
|
ncpu = 1 # number of cpu's on this platform
|
|
slen = 0 # size of shared data structure, incuding padding
|
|
cpu_1sec_usage = 0.0
|
|
cpu_10sec_usage = 0.0
|
|
heartbeat = 1
|
|
global dom_in_use, options
|
|
|
|
# mmap the (the first chunk of the) file
|
|
shmf = open(SHM_FILE, "r+")
|
|
shm = mmap.mmap(shmf.fileno(), QOS_DATA_SIZE)
|
|
|
|
# initialize curses
|
|
stdscr = _c.initscr()
|
|
_c.noecho()
|
|
_c.cbreak()
|
|
|
|
stdscr.keypad(1)
|
|
stdscr.timeout(1000)
|
|
[maxy, maxx] = stdscr.getmaxyx()
|
|
|
|
# display in a loop
|
|
while True:
|
|
|
|
cpuidx = 0
|
|
while cpuidx < ncpu:
|
|
|
|
# calculate offset in mmap file to start from
|
|
idx = cpuidx * slen
|
|
|
|
|
|
samples = []
|
|
doms = []
|
|
dom_in_use = []
|
|
domain_id = []
|
|
|
|
# read in data
|
|
for i in range(0, NSAMPLES):
|
|
len = struct.calcsize(ST_QDATA)
|
|
sample = struct.unpack(ST_QDATA, shm[idx:idx+len])
|
|
samples.append(sample)
|
|
idx += len
|
|
|
|
for i in range(0, NDOMAINS):
|
|
len = struct.calcsize(ST_DOM_INFO)
|
|
dom = struct.unpack(ST_DOM_INFO, shm[idx:idx+len])
|
|
doms.append(dom)
|
|
# (last_update_time, start_time, runnable_start_time, blocked_start_time,
|
|
# ns_since_boot, ns_oncpu_since_boot, runnable_at_last_update,
|
|
# runnable, in_use, domid, junk, name) = dom
|
|
# dom_in_use.append(in_use)
|
|
dom_in_use.append(dom[8])
|
|
domid = dom[9]
|
|
if domid == 32767 :
|
|
domid = IDLE_DOMAIN
|
|
domain_id.append(domid)
|
|
idx += len
|
|
# print "dom_in_use(cpu=%d): " % cpuidx, dom_in_use
|
|
|
|
|
|
len = struct.calcsize("4i")
|
|
oldncpu = ncpu
|
|
(next, ncpu, slen, freq) = struct.unpack("4i", shm[idx:idx+len])
|
|
idx += len
|
|
|
|
# xenbaked tells us how many cpu's it's got, so re-do
|
|
# the mmap if necessary to get multiple cpu data
|
|
if oldncpu != ncpu:
|
|
shm = mmap.mmap(shmf.fileno(), ncpu*slen)
|
|
|
|
# if we've just calculated data for the cpu of interest, then
|
|
# stop examining mmap data and start displaying stuff
|
|
if cpuidx == cpu:
|
|
break
|
|
|
|
cpuidx = cpuidx + 1
|
|
|
|
# calculate starting and ending datapoints; never look at "next" since
|
|
# it represents live data that may be in transition.
|
|
startat = next - 1
|
|
if next + 10 < NSAMPLES:
|
|
endat = next + 10
|
|
else:
|
|
endat = 10
|
|
|
|
# get summary over desired interval
|
|
[h1, l1, f1] = summarize(startat, endat, 10**9, samples)
|
|
[h2, l2, f2] = summarize(startat, endat, 10 * 10**9, samples)
|
|
|
|
|
|
# the actual display code
|
|
row = 0
|
|
display(stdscr, row, 1, "CPU = %d" % cpu, _c.A_STANDOUT)
|
|
|
|
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)
|
|
row +=1
|
|
display(stdscr, row, 1, "%s" % ((maxx-2)*'='))
|
|
|
|
total_h1_cpu = 0
|
|
total_h2_cpu = 0
|
|
|
|
cpu_1sec_usage = 0.0
|
|
cpu_10sec_usage = 0.0
|
|
|
|
for dom in range(0, NDOMAINS):
|
|
if not dom_in_use[dom]:
|
|
continue
|
|
|
|
if h1[dom][0][1] > 0 or domain_id[dom] == IDLE_DOMAIN:
|
|
# display gotten
|
|
row += 1
|
|
col = 2
|
|
display_domain_id(stdscr, row, col, domain_id[dom])
|
|
col += 4
|
|
display(stdscr, row, col, "%s" % time_scale(h2[dom][0][0]))
|
|
col += 12
|
|
display(stdscr, row, col, "%3.2f%%" % h2[dom][0][1])
|
|
if dom != IDLE_DOMAIN:
|
|
cpu_10sec_usage += h2[dom][0][1]
|
|
col += 12
|
|
display(stdscr, row, col, "%s/ex" % time_scale(h2[dom][0][2]))
|
|
col += 18
|
|
display(stdscr, row, col, "%s" % time_scale(h1[dom][0][0]))
|
|
col += 12
|
|
display(stdscr, row, col, "%3.2f%%" % h1[dom][0][1], _c.A_STANDOUT)
|
|
col += 12
|
|
display(stdscr, row, col, "%s/ex" % time_scale(h1[dom][0][2]))
|
|
col += 18
|
|
display(stdscr, row, col, "Gotten")
|
|
|
|
if dom != IDLE_DOMAIN:
|
|
cpu_1sec_usage = cpu_1sec_usage + h1[dom][0][1]
|
|
|
|
# display allocated
|
|
if options.allocated:
|
|
row += 1
|
|
col = 2
|
|
display_domain_id(stdscr, row, col, domain_id[dom])
|
|
col += 28
|
|
display(stdscr, row, col, "%s/ex" % time_scale(h2[dom][1]))
|
|
col += 42
|
|
display(stdscr, row, col, "%s/ex" % time_scale(h1[dom][1]))
|
|
col += 18
|
|
display(stdscr, row, col, "Allocated")
|
|
|
|
# display blocked
|
|
if options.blocked:
|
|
row += 1
|
|
col = 2
|
|
display_domain_id(stdscr, row, col, domain_id[dom])
|
|
col += 4
|
|
display(stdscr, row, col, "%s" % time_scale(h2[dom][2][0]))
|
|
col += 12
|
|
display(stdscr, row, col, "%3.2f%%" % h2[dom][2][1])
|
|
col += 12
|
|
display(stdscr, row, col, "%s/io" % time_scale(h2[dom][2][2]))
|
|
col += 18
|
|
display(stdscr, row, col, "%s" % time_scale(h1[dom][2][0]))
|
|
col += 12
|
|
display(stdscr, row, col, "%3.2f%%" % h1[dom][2][1])
|
|
col += 12
|
|
display(stdscr, row, col, "%s/io" % time_scale(h1[dom][2][2]))
|
|
col += 18
|
|
display(stdscr, row, col, "Blocked")
|
|
|
|
# display waited
|
|
if options.waited:
|
|
row += 1
|
|
col = 2
|
|
display_domain_id(stdscr, row, col, domain_id[dom])
|
|
col += 4
|
|
display(stdscr, row, col, "%s" % time_scale(h2[dom][3][0]))
|
|
col += 12
|
|
display(stdscr, row, col, "%3.2f%%" % h2[dom][3][1])
|
|
col += 12
|
|
display(stdscr, row, col, "%s/ex" % time_scale(h2[dom][3][2]))
|
|
col += 18
|
|
display(stdscr, row, col, "%s" % time_scale(h1[dom][3][0]))
|
|
col += 12
|
|
display(stdscr, row, col, "%3.2f%%" % h1[dom][3][1])
|
|
col += 12
|
|
display(stdscr, row, col, "%s/ex" % time_scale(h1[dom][3][2]))
|
|
col += 18
|
|
display(stdscr, row, col, "Waited")
|
|
|
|
# display ex count
|
|
if options.excount:
|
|
row += 1
|
|
col = 2
|
|
display_domain_id(stdscr, row, col, domain_id[dom])
|
|
|
|
col += 28
|
|
display(stdscr, row, col, "%d/s" % h2[dom][4])
|
|
col += 42
|
|
display(stdscr, row, col, "%d" % h1[dom][4])
|
|
col += 18
|
|
display(stdscr, row, col, "Execution count")
|
|
|
|
# display io count
|
|
if options.iocount:
|
|
row += 1
|
|
col = 2
|
|
display_domain_id(stdscr, row, col, domain_id[dom])
|
|
col += 4
|
|
display(stdscr, row, col, "%d/s" % h2[dom][5][0])
|
|
col += 24
|
|
display(stdscr, row, col, "%d/ex" % h2[dom][5][1])
|
|
col += 18
|
|
display(stdscr, row, col, "%d" % h1[dom][5][0])
|
|
col += 24
|
|
display(stdscr, row, col, "%3.2f/ex" % h1[dom][5][1])
|
|
col += 18
|
|
display(stdscr, row, col, "I/O Count")
|
|
|
|
#row += 1
|
|
#stdscr.hline(row, 1, '-', maxx - 2)
|
|
total_h1_cpu += h1[dom][0][1]
|
|
total_h2_cpu += h2[dom][0][1]
|
|
|
|
|
|
row += 1
|
|
star = heartbeat * '*'
|
|
heartbeat = 1 - heartbeat
|
|
display(stdscr, row, 1, star)
|
|
display(stdscr, row, 2, TOTALS % (total_h2_cpu, total_h1_cpu))
|
|
row += 1
|
|
# display(stdscr, row, 2,
|
|
# "\tFFP: %d (Min: %d, Max: %d)\t\t\tFFP: %d (Min: %d, Max %d)" %
|
|
# (math.ceil(f2[1]), f2[0], f2[2], math.ceil(f1[1]), f1[0], f1[2]), _c.A_BOLD)
|
|
|
|
if l1[1] > 1 :
|
|
row += 1
|
|
display(stdscr, row, 2,
|
|
"\tRecords lost: %d (Min: %d, Max: %d)\t\t\tRecords lost: %d (Min: %d, Max %d)" %
|
|
(math.ceil(l2[1]), l2[0], l2[2], math.ceil(l1[1]), l1[0], l1[2]), _c.A_BOLD)
|
|
|
|
# grab a char from tty input; exit if interrupt hit
|
|
try:
|
|
c = stdscr.getch()
|
|
except:
|
|
break
|
|
|
|
# q = quit
|
|
if c == ord('q'):
|
|
break
|
|
|
|
# c = cycle to a new cpu of interest
|
|
if c == ord('c'):
|
|
cpu = (cpu + 1) % ncpu
|
|
|
|
# n/p = cycle to the next/previous CPU
|
|
if c == ord('n'):
|
|
cpu = (cpu + 1) % ncpu
|
|
if c == ord('p'):
|
|
cpu = (cpu - 1) % ncpu
|
|
|
|
stdscr.erase()
|
|
|
|
_c.nocbreak()
|
|
stdscr.keypad(0)
|
|
_c.echo()
|
|
_c.endwin()
|
|
shm.close()
|
|
shmf.close()
|
|
|
|
|
|
# simple functions to allow initialization of log files without actually
|
|
# physically creating files that are never used; only on the first real
|
|
# write does the file get created
|
|
class Delayed(file):
|
|
def __init__(self, filename, mode):
|
|
self.filename = filename
|
|
self.saved_mode = mode
|
|
self.delay_data = ""
|
|
self.opened = 0
|
|
|
|
def delayed_write(self, str):
|
|
self.delay_data = str
|
|
|
|
def write(self, str):
|
|
if not self.opened:
|
|
self.file = open(self.filename, self.saved_mode)
|
|
self.opened = 1
|
|
self.file.write(self.delay_data)
|
|
self.file.write(str)
|
|
|
|
def rename(self, name):
|
|
self.filename = name
|
|
|
|
def flush(self):
|
|
if self.opened:
|
|
self.file.flush()
|
|
|
|
def close(self):
|
|
if self.opened:
|
|
self.file.close()
|
|
|
|
|
|
def writelog():
|
|
global options
|
|
global dom_in_use
|
|
|
|
ncpu = 1 # number of cpu's
|
|
slen = 0 # size of shared structure inc. padding
|
|
|
|
shmf = open(SHM_FILE, "r+")
|
|
shm = mmap.mmap(shmf.fileno(), QOS_DATA_SIZE)
|
|
|
|
interval = 0
|
|
curr = last = time.time()
|
|
outfiles = {}
|
|
for dom in range(0, NDOMAINS):
|
|
outfiles[dom] = Delayed("%s-dom%d.log" % (options.prefix, dom), 'w')
|
|
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")
|
|
|
|
while options.duration == 0 or interval < (options.duration * 1000):
|
|
cpuidx = 0
|
|
while cpuidx < ncpu:
|
|
|
|
idx = cpuidx * slen # offset needed in mmap file
|
|
|
|
samples = []
|
|
doms = []
|
|
dom_in_use = []
|
|
domain_id = []
|
|
|
|
for i in range(0, NSAMPLES):
|
|
len = struct.calcsize(ST_QDATA)
|
|
sample = struct.unpack(ST_QDATA, shm[idx:idx+len])
|
|
samples.append(sample)
|
|
idx += len
|
|
|
|
for i in range(0, NDOMAINS):
|
|
len = struct.calcsize(ST_DOM_INFO)
|
|
dom = struct.unpack(ST_DOM_INFO, shm[idx:idx+len])
|
|
# doms.append(dom)
|
|
# (last_update_time, start_time, runnable_start_time, blocked_start_time,
|
|
# ns_since_boot, ns_oncpu_since_boot, runnable_at_last_update,
|
|
# runnable, in_use, domid, junk, name) = dom
|
|
dom_in_use.append(dom[8])
|
|
domid = dom[9]
|
|
if domid == 32767:
|
|
domid = IDLE_DOMAIN
|
|
domain_id.append(domid)
|
|
if domid == IDLE_DOMAIN:
|
|
outfiles[i].rename("%s-idle.log" % options.prefix)
|
|
else:
|
|
outfiles[i].rename("%s-dom%d.log" % (options.prefix, domid))
|
|
idx += len
|
|
|
|
len = struct.calcsize("4i")
|
|
oldncpu = ncpu
|
|
(next, ncpu, slen, freq) = struct.unpack("4i", shm[idx:idx+len])
|
|
idx += len
|
|
|
|
if oldncpu != ncpu:
|
|
shm = mmap.mmap(shmf.fileno(), ncpu*slen)
|
|
|
|
startat = next - 1
|
|
if next + 10 < NSAMPLES:
|
|
endat = next + 10
|
|
else:
|
|
endat = 10
|
|
|
|
[h1,l1, f1] = summarize(startat, endat, options.interval * 10**6, samples)
|
|
for dom in range(0, NDOMAINS):
|
|
if not dom_in_use[dom]:
|
|
continue
|
|
if h1[dom][0][1] > 0 or dom == IDLE_DOMAIN:
|
|
outfiles[dom].write("%.3f %d %d %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f %.3f\n" %
|
|
(interval, cpuidx, domain_id[dom],
|
|
h1[dom][0][0], h1[dom][0][1], h1[dom][0][2],
|
|
h1[dom][1],
|
|
h1[dom][2][0], h1[dom][2][1], h1[dom][2][2],
|
|
h1[dom][3][0], h1[dom][3][1], h1[dom][3][2],
|
|
h1[dom][4],
|
|
h1[dom][5][0], h1[dom][5][1]))
|
|
outfiles[dom].flush()
|
|
curr = time.time()
|
|
interval += (curr - last) * 1000
|
|
last = curr
|
|
cpuidx = cpuidx + 1
|
|
time.sleep(options.interval / 1000.0)
|
|
|
|
for dom in range(0, NDOMAINS):
|
|
outfiles[dom].close()
|
|
|
|
# start xenbaked
|
|
def start_xenbaked():
|
|
global options
|
|
global kill_cmd
|
|
global xenbaked_cmd
|
|
|
|
os.system(kill_cmd)
|
|
os.system(xenbaked_cmd + " --ms_per_sample=%d &" %
|
|
options.mspersample)
|
|
time.sleep(1)
|
|
|
|
# stop xenbaked
|
|
def stop_xenbaked():
|
|
global stop_cmd
|
|
os.system(stop_cmd)
|
|
|
|
def main():
|
|
global options
|
|
global args
|
|
global domains
|
|
global stop_cmd
|
|
global kill_cmd
|
|
global xenbaked_cmd
|
|
|
|
if os.uname()[0] == "SunOS":
|
|
xenbaked_cmd = "/usr/lib/xenbaked"
|
|
stop_cmd = "/usr/bin/pkill -INT -z global xenbaked"
|
|
kill_cmd = "/usr/bin/pkill -KILL -z global xenbaked"
|
|
else:
|
|
# assumes that xenbaked is in your path
|
|
xenbaked_cmd = "xenbaked"
|
|
stop_cmd = "/usr/bin/pkill -INT xenbaked"
|
|
kill_cmd = "/usr/bin/pkill -KILL xenbaked"
|
|
|
|
parser = setup_cmdline_parser()
|
|
(options, args) = parser.parse_args()
|
|
|
|
if len(args):
|
|
parser.error("No parameter required")
|
|
if options.mspersample < 0:
|
|
parser.error("option --ms_per_sample: invalid negative value: '%d'" %
|
|
options.mspersample)
|
|
# If --ms_per_sample= is too large, no data may be logged.
|
|
if not options.live and options.duration != 0 and \
|
|
options.mspersample > options.duration * 1000:
|
|
parser.error("option --ms_per_sample: too large (> %d ms)" %
|
|
(options.duration * 1000))
|
|
|
|
start_xenbaked()
|
|
if options.live:
|
|
show_livestats(options.cpu)
|
|
else:
|
|
try:
|
|
writelog()
|
|
except:
|
|
print 'Quitting.'
|
|
stop_xenbaked()
|
|
|
|
if __name__ == "__main__":
|
|
main()
|