用Python可视化Profile
得益与Graphviz以及Python强劲的自省能力——Abettor GG,这就是你当年乐道的‘反射’啦,Shellex搞了一个可以生成Call Graph的东东。
谢谢Python-cn的各位同学给偶这个小菜菜的帮助,顺便过了一遍《源码剖析》的第八章。
class call_tracker:
def __init__(self, cls, logfile='track.log'):
self.stack = [('Push', 'start')]
self.call_map = {}
cls = cls if isinstance(cls, list) else [cls]
calls = reduce(lambda a, b: a.__add__(b), [self.get_calls(c) for c in cls])
for type, name, call in calls:
call = self.hook(call)
setattr(type, name, call)
self.reg = re.compile('<(.*?) (.*?) (.*?)>')
def __del__(self):
pass
def get_calls(self, cls):
return filter(lambda (type, name, addr): callable(addr), [(cls, x, getattr(cls, x)) for x in dir(cls)])
def get_func_name(self, proc):
return self.reg.search(proc.__str__()).group(3)
def hook(self, proc):
def uname(*args):
proc_name = self.get_func_name(proc)
#print '[Entry] %s' % ( proc_name)
action, parent = self.stack[-1]
self.stack.append(('Push', proc_name))
start_t = time.time()
ret = proc(*args)
cost_t = (time.time() - start_t) * 1000
self.stack.pop()
if action == 'Push':
if not self.call_map.has_key((parent, proc_name)):
cnt = 1
else:
cnt, cost = self.call_map[(parent, proc_name)]
cnt += 1
self.call_map[(parent, proc_name)] = (cnt, cost_t)
#print '[Leave] %s' % ( proc_name)
return ret
return uname
def write_png(self, png):
out = 'digraph %s { \n' % png.replace('.', '_')
out += 'rankdir=LR;\n'
for (frm, to), (cnt, cost) in self.call_map.iteritems():
out += '"%s" -> "%s" [label="%s(%.3fms)"];\n' % (frm, to, cnt, cost)
out += '}\n'
dot = open(png+'.gv', 'w+')
dot.write(out)
dot.close()
from os import popen2
popen2('/usr/bin/dot -Tpng %s -o %s' % (png+'.gv', png))
#popen2('/usr/bin/eog ./%s' % png)


