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