ia64/xen-unstable

view tools/xenmon/xenmon.py @ 16193:b28ae5f00553

xenmon: Fix security vulnerability CVE-2007-3919.

The xenbaked daemon and xenmon utility communicate via a mmap'ed
shared file. Since this file is located in /tmp, unprivileged users
can cause arbitrary files to be truncated by creating a symlink from
the well-known /tmp filename to e.g., /etc/passwd.

The fix is to place the shared file in a directory to which only root
should have access (in this case /var/run/).

This bug was reported, and the fix suggested, by Steve Kemp
<skx@debian.org>. Thanks!

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