Show More
@@ -433,6 +433,7 b' class DisplayFormats:' | |||||
433 | Hotpath = 3 |
|
433 | Hotpath = 3 | |
434 | FlameGraph = 4 |
|
434 | FlameGraph = 4 | |
435 | Json = 5 |
|
435 | Json = 5 | |
|
436 | Chrome = 6 | |||
436 |
|
437 | |||
437 | def display(fp=None, format=3, data=None, **kwargs): |
|
438 | def display(fp=None, format=3, data=None, **kwargs): | |
438 | '''Print statistics, either to stdout or the given file object.''' |
|
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 | write_to_flame(data, fp, **kwargs) |
|
458 | write_to_flame(data, fp, **kwargs) | |
458 | elif format == DisplayFormats.Json: |
|
459 | elif format == DisplayFormats.Json: | |
459 | write_to_json(data, fp) |
|
460 | write_to_json(data, fp) | |
|
461 | elif format == DisplayFormats.Chrome: | |||
|
462 | write_to_chrome(data, fp, **kwargs) | |||
460 | else: |
|
463 | else: | |
461 | raise Exception("Invalid display format") |
|
464 | raise Exception("Invalid display format") | |
462 |
|
465 | |||
463 |
if format |
|
466 | if format not in (DisplayFormats.Json, DisplayFormats.Chrome): | |
464 | print('---', file=fp) |
|
467 | print('---', file=fp) | |
465 | print('Sample count: %d' % len(data.samples), file=fp) |
|
468 | print('Sample count: %d' % len(data.samples), file=fp) | |
466 | print('Total time: %f seconds' % data.accumulated_time, file=fp) |
|
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 | print(json.dumps(samples), file=fp) |
|
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 | def printusage(): |
|
845 | def printusage(): | |
747 | print(""" |
|
846 | print(""" | |
748 | The statprof command line allows you to inspect the last profile's results in |
|
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