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