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