##// END OF EJS Templates
statprof: allow rendering in the Chrome trace viewer format
Bryan O'Sullivan -
r30929:cb440e7a default
parent child Browse files
Show More
@@ -433,6 +433,7 b' class DisplayFormats:'
433 433 Hotpath = 3
434 434 FlameGraph = 4
435 435 Json = 5
436 Chrome = 6
436 437
437 438 def display(fp=None, format=3, data=None, **kwargs):
438 439 '''Print statistics, either to stdout or the given file object.'''
@@ -457,10 +458,12 b' def display(fp=None, format=3, data=None'
457 458 write_to_flame(data, fp, **kwargs)
458 459 elif format == DisplayFormats.Json:
459 460 write_to_json(data, fp)
461 elif format == DisplayFormats.Chrome:
462 write_to_chrome(data, fp, **kwargs)
460 463 else:
461 464 raise Exception("Invalid display format")
462 465
463 if format != DisplayFormats.Json:
466 if format not in (DisplayFormats.Json, DisplayFormats.Chrome):
464 467 print('---', file=fp)
465 468 print('Sample count: %d' % len(data.samples), file=fp)
466 469 print('Total time: %f seconds' % data.accumulated_time, file=fp)
@@ -743,6 +746,102 b' def write_to_json(data, fp):'
743 746
744 747 print(json.dumps(samples), file=fp)
745 748
749 def write_to_chrome(data, fp, minthreshold=0.005, maxthreshold=0.999):
750 samples = []
751 laststack = collections.deque()
752 lastseen = collections.deque()
753
754 # The Chrome tracing format allows us to use a compact stack
755 # representation to save space. It's fiddly but worth it.
756 # We maintain a bijection between stack and ID.
757 stack2id = {}
758 id2stack = [] # will eventually be rendered
759
760 def stackid(stack):
761 if not stack:
762 return
763 if stack in stack2id:
764 return stack2id[stack]
765 parent = stackid(stack[1:])
766 myid = len(stack2id)
767 stack2id[stack] = myid
768 id2stack.append(dict(category=stack[0][0], name='%s %s' % stack[0]))
769 if parent is not None:
770 id2stack[-1].update(parent=parent)
771 return myid
772
773 def endswith(a, b):
774 return list(a)[-len(b):] == list(b)
775
776 # The sampling profiler can sample multiple times without
777 # advancing the clock, potentially causing the Chrome trace viewer
778 # to render single-pixel columns that we cannot zoom in on. We
779 # work around this by pretending that zero-duration samples are a
780 # millisecond in length.
781
782 clamp = 0.001
783
784 # We provide knobs that by default attempt to filter out stack
785 # frames that are too noisy:
786 #
787 # * A few take almost all execution time. These are usually boring
788 # setup functions, giving a stack that is deep but uninformative.
789 #
790 # * Numerous samples take almost no time, but introduce lots of
791 # noisy, oft-deep "spines" into a rendered profile.
792
793 blacklist = set()
794 totaltime = data.samples[-1].time - data.samples[0].time
795 minthreshold = totaltime * minthreshold
796 maxthreshold = max(totaltime * maxthreshold, clamp)
797
798 def poplast():
799 oldsid = stackid(tuple(laststack))
800 oldcat, oldfunc = laststack.popleft()
801 oldtime, oldidx = lastseen.popleft()
802 duration = sample.time - oldtime
803 if minthreshold <= duration <= maxthreshold:
804 # ensure no zero-duration events
805 sampletime = max(oldtime + clamp, sample.time)
806 samples.append(dict(ph='E', name=oldfunc, cat=oldcat, sf=oldsid,
807 ts=sampletime*1e6, pid=0))
808 else:
809 blacklist.add(oldidx)
810
811 # Much fiddling to synthesize correctly(ish) nested begin/end
812 # events given only stack snapshots.
813
814 for sample in data.samples:
815 tos = sample.stack[0]
816 name = tos.function
817 path = simplifypath(tos.path)
818 category = '%s:%d' % (path, tos.lineno)
819 stack = tuple((('%s:%d' % (simplifypath(frame.path), frame.lineno),
820 frame.function) for frame in sample.stack))
821 qstack = collections.deque(stack)
822 if laststack == qstack:
823 continue
824 while laststack and qstack and laststack[-1] == qstack[-1]:
825 laststack.pop()
826 qstack.pop()
827 while laststack:
828 poplast()
829 for f in reversed(qstack):
830 lastseen.appendleft((sample.time, len(samples)))
831 laststack.appendleft(f)
832 path, name = f
833 sid = stackid(tuple(laststack))
834 samples.append(dict(ph='B', name=name, cat=path, ts=sample.time*1e6,
835 sf=sid, pid=0))
836 laststack = collections.deque(stack)
837 while laststack:
838 poplast()
839 events = [s[1] for s in enumerate(samples) if s[0] not in blacklist]
840 frames = collections.OrderedDict((str(k), v)
841 for (k,v) in enumerate(id2stack))
842 json.dump(dict(traceEvents=events, stackFrames=frames), fp, indent=1)
843 fp.write('\n')
844
746 845 def printusage():
747 846 print("""
748 847 The statprof command line allows you to inspect the last profile's results in
General Comments 0
You need to be logged in to leave comments. Login now