#slprofile, the profile module adapted for stackless and blue import profile #This class provides the start() and stop() functions class Profile(profile.Profile): base = profile.Profile def __init__(self, timer = None, bias=None): if timer is None: timer = Timer() self.base.__init__(self, timer, bias) self.sleeping = {} self.oldtimings = [] #timings info from dead tasklets. def __call__(self, *args): "make callable, allowing an instance to be the profiler" r = self.dispatcher(*args) def AddCtxHook(self, add): try: import blue if add: blue.pyos.contextHooks.append(self.context_dispatcher) else: blue.pyos.contextHooks.remove(self.context_dispatcher) self.TallyTimings() #tally the timings except ImportError: pass def start(self, name = "start"): self.set_cmd(name) import sys self.AddCtxHook(True) sys.setprofile(self.dispatcher) def stop(self): import sys sys.setprofile(None) self.AddCtxHook(False) def runctx(self, cmd, globals, locals): self.AddCtxHook(True) try: profile.Profile.runctx(self, cmd, globals, locals) finally: self.AddCtxHook(False) def runcall(self, func, *args, **kw): self.AddCtxHook(True) try: profile.Profile.runcall(self, func, *args, **kw) finally: self.AddCtxHook(False) def context_dispatcher(self, old, new): "A helper function, that wraps the context arguments for the standardized format, so the regular dispatcher can be used" import sys sys.setprofile(None) #turn off the profiler. we are not in a profiler callback here! try: try: self.dispatcher((old, new), "context", None) except: import traceback traceback.print_exc() finally: sys.setprofile(self.dispatcher) def trace_dispatch_return_ignore_badreturns(self, frame, t): """A hack function to override error checking in parent class. It allows invalid returns as can happen when we create a cur frame in the middle of a callstack which subsequently returns a lot.""" if not self.cur[-1]: return False #don't try to return from the faked call last = self.cur[-2] if frame is not last: if not self.cur[-1][-1] or frame is not last.f_back: return False return self.trace_dispatch_return(frame, t); def trace_dispatch_context(self, tasklets, t): t0, t1 = tasklets #tally the time pt, it, et, fn, frame, rcur = self.cur cur = (pt, it+t, et, fn, frame, rcur) if t1: #we are switghing to a new tasklet, store the old self.sleeping[t0] = cur, self.timings else: #this tasklet is dying, store it away self.oldtimings.append((cur, self.timings)) #find the new one. This may be the null tasklet. found = self.sleeping.get(t1, None) if found: del self.sleeping[t1] self.cur, self.timings = found else: self.timings = {} self.cur = None if t1: name = "new_tasklet" else: name = "null tasklet" self.simulate_call(name) return True dispatch = { "call": base.trace_dispatch_call, "exception": base.trace_dispatch_exception, "return": trace_dispatch_return_ignore_badreturns, "context": trace_dispatch_context, } def TallyTimings(self): self.oldtimings.extend(self.sleeping.values()) self.sleeping.clear() #at this point, self.timings has 'nc' as the second element in the tuple. #however, oldtimings has ns as the second element, it hasn't been processed yet. for cur,timings in self.oldtimings: self.Unwind(cur, timings) self.ProcessTimings(timings) for k,v in timings.iteritems(): if k not in self.timings: self.timings[k] = v else: cc, nc, tt, ct, callers = self.timings[k] cc+=v[0] nc+=v[1] tt+=v[2] ct+=v[3] for k1,v1 in v[4].iteritems(): if k1 not in callers: callers[k1] = v1 else: callers[k1] += v1 self.timings[k] = cc, nc, tt, ct, callers self.oldtimings = [] def Unwind(self, cur, timings): "A function to unwind a 'cur' frame and tally the results" "see profile.trace_dispatch_return() for details" while(cur): rpt, rit, ret, rfn, frame, rcur = cur frame_total = rit+ret if rfn in timings: cc, ns, tt, ct, callers = timings[rfn] else: cc, ns, tt, ct, callers = 0, 0, 0, 0, {} if not ns: ct = ct + frame_total cc = cc + 1 if rcur: ppt, pit, pet, pfn, pframe, pcur = rcur else: pfn = None if pfn in callers: callers[pfn] = callers[pfn] + 1 # hack: gather more elif pfn: callers[pfn] = 1 timings[rfn] = cc, ns - 1, tt + rit, ct, callers if rcur: ppt, pit, pet, pfn, pframe, pcur = rcur rcur = ppt, pit + rpt, pet + frame_total, pfn, pframe, pcur cur = rcur def ProcessTimings(self, timings) : #compute total calls (as opposed to non-recursive calls) new = {} for k,v in timings.iteritems(): #compute total calls cc, ns, tt, ct, callers = v nc = 0 for callcnt in callers.itervalues(): nc+=callcnt v = cc, nc, tt, ct, callers new[k] = v timings.update(new) class Timer(object): #This class creates a seconds timer initialized to zero on creation, using the hypersensitive bluetimer if possible. def __init__(self): try: import blue self.startTime = blue.os.GetTime(1) except: import time self.startTime = time.clock() def TimeBlue(self): import blue return float(blue.os.GetTime(1) - self.startTime) *1e-7 def Fallback(self): import time return time.clock() - self.startTime try: import blue except: GetTime = Fallback else: GetTime = TimeBlue __call__ = GetTime