| #!/usr/bin/env python | |
| # Copyright (C) 2015 Mark Hahnenberg. All rights reserved. | |
| # | |
| # Redistribution and use in source and binary forms, with or without | |
| # modification, are permitted provided that the following conditions | |
| # are met: | |
| # 1. Redistributions of source code must retain the above copyright | |
| # notice, this list of conditions and the following disclaimer. | |
| # 2. Redistributions in binary form must reproduce the above copyright | |
| # notice, this list of conditions and the following disclaimer in the | |
| # documentation and/or other materials provided with the distribution. | |
| # | |
| # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' | |
| # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, | |
| # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
| # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS | |
| # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
| # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
| # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
| # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
| # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
| # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
| # THE POSSIBILITY OF SUCH DAMAGE. | |
| import fileinput | |
| import re | |
| TIMING_REGEX = re.compile( | |
| r'^\[(?P<pid>[0-9]+)\] ' | |
| '(?P<name>[^ ]+) ' | |
| '\(Parent: (?P<parent>[^\)]+)\) ' | |
| '\((?P<collect_type>[^\)]+)\): ' | |
| '(?P<total_time>[0-9]+\.[0-9]+)ms ' | |
| '\(avg. (?P<avg_time>[^,]+), ' | |
| 'min. (?P<min_time>[^,]+), ' | |
| 'max. (?P<max_time>[^,]+), ' | |
| 'count (?P<count>[^\)]+)\)') | |
| class Timing(object): | |
| def __init__(self, pid, name, parent, collect_type, total_time, avg_time, min_time, max_time, count): | |
| self.pid = int(pid) | |
| self.name = str(name) | |
| self.parent = str(parent) | |
| self.collect_type = str(collect_type) | |
| self.total_time = float(total_time) | |
| self.avg_time = float(avg_time) | |
| self.min_time = float(min_time) | |
| self.max_time = float(max_time) | |
| self.count = int(count) | |
| self.children = [] | |
| def __unicode__(self): | |
| return u"%s - %s total: %.2f, avg: %.2f" % (self.name, self.collect_type, self.total_time, self.avg_time) | |
| def __str__(self): | |
| return "%s - %s total: %.2f, avg: %.2f" % (self.name, self.collect_type, self.total_time, self.avg_time) | |
| def __repr__(self): | |
| return "%s - %s total: %.2f, avg: %.2f" % (self.name, self.collect_type, self.total_time, self.avg_time) | |
| def parse_input(): | |
| timings = [] | |
| for line in fileinput.input(): | |
| result = TIMING_REGEX.match(line) | |
| if result is None: | |
| continue | |
| timings.append(Timing( | |
| result.group('pid'), | |
| result.group('name'), | |
| result.group('parent'), | |
| result.group('collect_type'), | |
| result.group('total_time'), | |
| result.group('avg_time'), | |
| result.group('min_time'), | |
| result.group('max_time'), | |
| result.group('count'), | |
| )) | |
| return timings | |
| def print_timing_node(root, timings, tabs): | |
| for _ in range(tabs): | |
| print " ", | |
| percent_time = 1.0 | |
| if root.parent is not None: | |
| percent_time = float(root.total_time) / float(root.parent.total_time) | |
| print "%s - %.2f%%" % (str(root), percent_time * 100.0) | |
| for child in reversed(sorted(root.children, key=lambda t: t.total_time)): | |
| if child.parent != root: | |
| continue | |
| if child.collect_type != root.collect_type: | |
| continue | |
| print_timing_node(child, timings, tabs + 1) | |
| def print_timing_tree(timings): | |
| timings.sort(key=lambda t: t.total_time) | |
| timings.reverse() | |
| collection_types = ["All", "Eden", "Full"] | |
| for collect_type in collection_types: | |
| for timing in timings: | |
| if timing.collect_type != collect_type: | |
| continue | |
| if timing.parent is not None: | |
| continue | |
| print_timing_node(timing, timings, 0) | |
| print "" | |
| def link_parents(timings): | |
| for timing in timings: | |
| if timing.parent == "nullptr": | |
| timing.parent = None | |
| continue | |
| for parent in timings: | |
| if timing.parent != parent.name: | |
| continue | |
| if timing.collect_type != parent.collect_type: | |
| continue | |
| timing.parent = parent | |
| parent.children.append(timing) | |
| def main(): | |
| timings = parse_input() | |
| link_parents(timings) | |
| print_timing_tree(timings) | |
| if __name__ == "__main__": | |
| main() |