5 __version__ =
"$Revision: 524466 $"
6 __doc__ =
"various utils to encode/decode perfmon (meta)data with base64"
7 __author__ =
"Sebastien Binet, Thomas Kittlemann"
12 return a dict of string->file-like objects containing the
13 perfmon data and metadata
15 if not fname.endswith(
'pmon.gz'):
16 raise ValueError(
"expect a xyz.pmon.gz file (got [%s])"%(fname,))
21 tmpdir = tempfile.mkdtemp(prefix=
'pmon_ser_')
24 def cleanup_pmon_ser(tmpdir=tmpdir):
26 if os.path.exists(tmpdir):
28 except Exception
as err:
29 print(
"pmon.cleanup_pmon_ser: **warning**:\n%s", (err,), file=sys.stderr)
31 atexit.register(cleanup_pmon_ser)
33 tar = tarfile.open(fname,
'r')
34 tar.extractall(path=tmpdir)
38 files = glob.glob(os.path.join(tmpdir,
'*.stream'))
39 stream_fname = files[0]
41 files = glob.glob(os.path.join(tmpdir,
"*.dat"))
44 files = glob.glob(os.path.join(tmpdir,
"*.pmonsd.txt"))
45 semidet_fname = files[0]
if files
else None
48 'infos':
open(dat_fname),
49 'data':
open(stream_fname),
50 'semidet' :
open(semidet_fname)
if semidet_fname
else None
54 """ extract the perfmon informations from ``fname``
55 if ``fname`` ends with '.pmon.dat', assume it is a shelve.
56 otherwise, assumes it's a tar-ball with a .pmon.dat shelve inside.
58 returns None in case of errors
61 def dict_from_shelve(fname):
63 if open(fname,
'rb').
read(1024).startswith(b
'SQLite format'):
64 import PyUtils.dbsqlite
as dbs
65 db = dbs.open(fname,
'r')
69 db = shelve.open(fname)
78 if 'perf_summary' in out.keys():
79 return {
'perf_summary':out[
'perf_summary']}
84 if fname.endswith(
'pmon.dat'):
85 return dict_from_shelve(fname)
88 infos_file = o[
'infos']
89 out = dict_from_shelve(infos_file.name)
93 from collections
import defaultdict
94 out = defaultdict(dict)
97 iocpu_dtype = np.dtype([
104 cpu_dtype = np.dtype([
111 mem_dtype = np.dtype([
112 (
'vmem',
'3float32'),
114 (
'mall',
'3float32'),
118 comp_dtype=np.dtype([(
'cpu', cpu_dtype), (
'mem', mem_dtype)])
121 return np.zeros(0, dtype=comp_dtype)
123 iocomp_dtype = np.dtype([(
'r', iocpu_dtype),
125 (
'w', iocpu_dtype),])
127 return np.zeros(0, dtype=iocomp_dtype)
129 for step
in (
'ini',
'evt',
'fin',
'cbk',
'usr',
130 'preLoadProxy',
'dso',):
131 out[step] = defaultdict(new_component)
133 out[step] = defaultdict(new_iocomp)
139 iterates over the file pointed at by `fname` and yields a tuple of
140 (step, idx, comp, out)
142 - `step` is in ('ini','evt','fin','cbk','usr','dso',...)
143 - `idx` is 0 or 1 (resp. start or stop)
144 - `comp` is the name of the entity being monitored (alg,library,cbk,...)
145 - `out` is the up-to-date table of data collected so far
147 the table is a dict of {'step':{'comp':numpy.array}}
149 this table is the suitable python object for further analyses
152 if fname.endswith(
'.pmon.gz'):
154 stream_file = o[
'data']
155 stream_fname = stream_file.name
156 elif fname.endswith(
'.stream'):
157 stream_file =
open(fname)
160 raise ValueError(
"expect a xyz.pmon.gz or xyz.stream file (got [%s])"%(fname,))
165 with open(stream_fname,
'r')
as f:
167 data, step, idx, comp = (
None, ) * 4
168 if z.startswith(
'#'):
174 z = z.replace(
'unsigned int',
'unsigned-int')\
179 if fields[0].startswith((
'/ini/',
'/evt/',
'/fin/',
193 fields[0] = fields[0]+fields[1]
200 step = fields[0][1:-1]
201 step = step.split(
'/')[0]
202 comp = fields[0][len(step)+2:]
210 offset = comp.split(
']+')[1]
211 bracket_idx = comp.find(
'[')
213 comp =
'%s{+%s}' % (comp[:bracket_idx],offset)
217 data = out[step][comp]
221 data = np.zeros(1, data.dtype)
222 data = out[step][comp] = np.append(out[step][comp],
228 cpu[
'user'][idx] =
float(fields[2])
229 cpu[
'sys'][idx] =
float(fields[3])
230 cpu[
'real'][idx] =
float(fields[4])
231 cpu[
'rt'][idx] =
float(fields[5])
232 cpu[
'cpu'][idx] = cpu[
'user'][idx] + cpu[
'sys'][idx]
235 mem[
'vmem'][idx] =
float(fields[6])/1024.
236 mem[
'rss'][idx] =
float(fields[7])/1024.
237 mem[
'mall'][idx] =
float(fields[8])/1024.
238 mem[
'nmall'][idx]=
float(fields[9])
239 mem[
'nfree'][idx]=
float(fields[10])
244 for n
in d.dtype.names:
246 d[n][1] = d[n][0] + d[n][1]
248 for n
in d.dtype.names:
249 d[n][2] = d[n][1] - d[n][0]
252 elif fields[0].startswith(
'/io/'):
254 comp = fields[0][len(step)+2:]
255 data = out[step][comp]
257 data = np.zeros(1, data.dtype)
259 data = out[step][comp] = np.append(out[step][comp],
264 r[
'user'] =
float(fields[1])
265 r[
'sys'] =
float(fields[2])
266 r[
'real'] =
float(fields[3])
267 r[
'cpu'] = r[
'user'] + r[
'sys']
268 r[
'rt'] =
float(fields[4])
271 rr[
'user'] =
float(fields[5])
272 rr[
'sys'] =
float(fields[6])
273 rr[
'real'] =
float(fields[7])
274 rr[
'cpu'] = rr[
'user'] + rr[
'sys']
275 rr[
'rt'] =
float(fields[8])
278 w[
'user'] =
float(fields[9])
279 w[
'sys'] =
float(fields[10])
280 w[
'real'] =
float(fields[11])
281 w[
'cpu'] = w[
'user'] + w[
'sys']
282 w[
'rt'] =
float(fields[12])
286 elif fields[0].startswith(
'/dso/'):
288 comp = fields[0][len(step)+2:]
289 data = out[step][comp]
292 data = out[step][comp]
296 data = np.zeros(1, data.dtype)
297 data = out[step][comp] = np.append(out[step][comp],
302 if len(fields) >= 10:
304 cpu[
'user'][idx] =
float(fields[2])
305 cpu[
'sys'][idx] =
float(fields[3])
306 cpu[
'real'][idx] =
float(fields[4])
307 cpu[
'rt'][idx] =
float(fields[5])
308 cpu[
'cpu'][idx] = cpu[
'user'][idx] + cpu[
'sys'][idx]
311 mem[
'vmem'][idx] =
float(fields[6])/1024.
312 mem[
'rss'][idx] =
float(fields[7])/1024.
313 mem[
'mall'][idx] =
float(fields[8])/1024.
314 mem[
'nmall'][idx]=
float(fields[9])
315 mem[
'nfree'][idx]=
float(fields[10])
319 cpu[
'user'][idx] = 0.
321 cpu[
'real'][idx] = 0.
326 mem[
'vmem'][idx] =
float(fields[2])/1024.
328 mem[
'mall'][idx] = 0.
329 mem[
'nmall'][idx]= 0.
330 mem[
'nfree'][idx]= 0.
335 for n
in d.dtype.names:
337 d[n][1] = d[n][0] + d[n][1]
339 for n
in d.dtype.names:
340 d[n][2] = d[n][1] - d[n][0]
344 print(
"warning: unhandled field [%s]" % (fields[0],))
348 yield step, idx, comp, out
355 return a suitable python object for further analyses from a pmon file
364 out[k] =
dict(out[k])
368 """ load all the pmon data from a .pmon.gz file ``fname`` and return
369 the tuple (infos, data)
376 """encode some data into a (compressed) string, using base64 (or not)
380 import cPickle
as pickle
381 s=zlib.compress(pickle.dumps(data,1),6)
384 return 'B'+base64.b64encode(s)
389 """decode a (compressed) string into a python object
394 import cPickle
as pickle
397 s=base64.b64decode(s[1:])
400 return pickle.loads(zlib.decompress(s))
403 """ take a `fname` pmon file, iterate over its data and return a callgraph
404 for each of the 'ini', 'evt' and 'fin' steps.
405 all over pmon steps are folded into the 3 above ones.
406 each event during the 'evt' step has its own callgraph structure
419 if comp ==
'PerfMonSliceIo':
427 'logic error step=[%s], idx=[%s], comp=[%s]' %
431 if comp
in (
'PerfMonSvc',
'AuditorSvc'):
438 if step != current_step
and step
in graph.keys():
440 if current_step ==
'ini' and step ==
'evt':
443 elif current_step ==
'evt' and step ==
'fin':
449 graph[current_step].
append(local_ctx)
451 elif current_step ==
'evt' and step ==
'ini':
457 'unknown transition [%s -> %s]' % (current_step, step)
460 if comp ==
'PerfMonSlice':
462 if current_step !=
'fin':
466 graph[current_step].
append(local_ctx)
476 local_ctx.data_idx = (step, comp, idx)
477 local_ctx.data = table[step][comp][-1]
481 if current_step ==
'fin':
485 raise RuntimeError(
'unknown index [%s]' % (idx,))
489 parent_ctx = local_ctx
490 local_ctx =
GraphNode(comp, parent=parent_ctx)
491 if step
not in graph.keys():
492 local_ctx.ctype = step
493 parent_ctx.children.append(local_ctx)
495 if comp != local_ctx.name:
497 'closing context [%s] within scope of [%s]' %
498 (comp, local_ctx.name)
500 assert local_ctx.parent,
"unrooted local context [%r]" % (
502 local_ctx.data_idx = (step, comp, idx)
503 local_ctx.data = table[step][comp][-1]
505 local_ctx = local_ctx.parent
507 raise RuntimeError(
'unknown index [%s]' % (idx,))
515 """helper function to iterate over a bunch of GraphNodes
516 @return a tuple (node, nest-level)
518 def _igraph(node, indent=0):
520 for c
in node.children:
521 for cc
in _igraph(c,indent+1):
526 """return the monitoring data attached to a node of the callgraph
527 ``op_name`` can be: 'self' or 'incl'
529 assert op_name
in (
'self',
'incl'),\
530 'op_name must be either \'self\' or \'incl\''
533 op = getattr(node, op_name)
534 if node.ctype
in (
'comp',
'dso',
535 'usr',
'preLoadProxy',
'cbk'):
536 cpu_usr = op(
'cpu',
'user',2)
537 cpu_sys = op(
'cpu',
'sys', 2)
538 cpu_real= op(
'cpu',
'real',2)
539 cpu_cpu = op(
'cpu',
'cpu', 2)
540 mem_vmem= op(
'mem',
'vmem',2)
541 mem_rss= op(
'mem',
'rss', 2)
542 mem_mall= op(
'mem',
'mall',2)
543 mem_nmall=op(
'mem',
'nmall',2)
544 mem_nfree=op(
'mem',
'nfree',2)
545 data= (cpu_cpu, mem_vmem, cpu_usr, cpu_sys, cpu_real,
546 mem_rss, mem_mall, mem_nmall, mem_nfree)
548 raise RuntimeError(
'unknown node type [%s]' % node.ctype)
549 return ' '.
join(map(str,map(int,data)))
552 f =
open(oname,
'w+b')
555 def _get_data(node, op_name='self'):
558 except Exception
as err:
560 print(
"node=%r" % node)
566 if node.ctype
in (
'dso',
'usr',
'preLoadProxy',
'cbk'):
569 return metadata[node.name]
575 return _get_meta(node.parent)
581 write(
"events: cpu_cpu mem_vmem cpu_user cpu_sys cpu_real "
582 "mem_rss mem_malloc mem_nallocs mem_nfrees\n")
587 line_nbr = collections.defaultdict(itertools.count().next)
594 "%i %s\n" % (node.ctype,
598 _get_data(node, op_name=
'self'),)
600 for c
in node.children:
605 "calls=%i %i\n%i %s\n" % (
609 1, line_nbr[c.name], line_nbr[node.name],
610 _get_data(c, op_name=
'incl')
618 """take (the path to) a pmon file name `fname` and create a bunch of
620 - one for the 'ini' stage
622 - one for the 'fin' stage
623 the cachegrind files will be named like so:
625 <fname>.evt.xxxx.cgc.out
631 if domainsdb
is None:
633 for n
in (
'PerfMonSlice',
'AthMasterSeq',
637 domainsdb[n] =
'admin'
639 tmpl_name = fname.replace(
'.pmon.gz',
'.cgc.out')
641 tmpl_name.replace(
'.cgc',
'.ini.cgc'),
643 for ievt, graph
in enumerate(root_graph[
'evt']):
646 tmpl_name.replace(
'.cgc.out',
647 '.evt.%s.cgc.out' %
str(ievt).zfill(4)),
651 tmpl_name.replace(
'.cgc.out',
'.fin.cgc.out'),
658 def __init__(self, name, parent=None, children=None,
666 self.
ctype = comp_type
669 parent_name =
'<root>'
671 parent_name = self.
parent.name
672 return '<GraphNode[%s] p=[%s] c=[%s] type=[%s]>' % (
691 raise IndexError(
'no such index %r' % (tuple(idx),))
704 incl = self.
incl(*idx)
705 excl = self.
excl(*idx)
710 'incl': self.
incl(*idx),
711 'excl': self.
excl(*idx),
712 'self': self.
self(*idx),
725 if __name__==
'__main__':
730 print(
"Please supply one or more perfmon files to investigate (*.pmon, *.pmon.gz or *.pmon.dat)")
732 for arg
in sys.argv[1:]:
733 print(
"Investigating file",arg)
734 if not os.path.exists(arg):
735 print(
" ==> Not found!")
738 for only_summary
in [
True,
False]:
740 for use_base64
in [
False,
True]:
741 s_info=
"summary" if only_summary
else 'full'
742 s_enc=
"base64" if use_base64
else 'binary'
743 p =
encode(data=infodict,use_base64=use_base64)
744 s=
' ==> Reading '+s_info+
' info with '+s_enc+
' encoding: <'+(
str(len(p))+
' bytes'if p
else 'failed')+
'>'
747 decodeOk = (decoded==infodict)
748 s+=
' [Decoding '+(
'OK' if decodeOk
else 'FAILED')+
']'
755 import PerfMonComps.PerfMonSerializer as pmon_ser
756 infos,data = pmon_ser.pmon_load('foo.pmon.gz')
757 ddb = infos['domains_a2d']
758 root_graph = pmon_ser.build_callgraph('foo.pmon.gz')
759 evt = root_graph['evt'][-1]
760 domains_stats = collections.defaultdict(float)
761 for c in evt[0][0].children:
765 domains_stats[domain] += c.incl('cpu','cpu',2)
767 print('**err**',c.name)
769 print(dict(domains_stats))