#!/usr/bin/env python3 # Copyright 2021 Google LLC # # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. # This script is written to process the output from bloaty, read via stdin # The easiest way to use the script: # # bloaty -d compileunits,symbols -n 0 --tsv | bloaty_treemap.py > bloaty.html # # Open the resulting .html file in your browser. # TODO: Deal with symbols vs. fullsymbols, even both? # TODO: Support aggregation by scope, rather than file (split C++ identifiers on '::') # TODO: Deal with duplicate symbols better. These are actually good targets for optimization. # They are sometimes static functions in headers (so they appear in multiple .o files), # There are also symbols that appear multiple times due to inlining (eg, kNoCropRect). # TODO: Figure out why some symbols are misattributed. Eg, Swizzle::Convert and ::Make are tied # to the header by nm, and then to one caller (at random) by bloaty. They're not inlined, # though. Unless LTO is doing something wacky here? Scope-aggregation may be the answer? # Ultimately, this seems like an issue with bloaty and/or debug information itself. import os import sys parent_map = {} # For a given filepath "foo/bar/baz.cpp", `add_path` outputs rows to the data table # establishing the node hierarchy, and ensures that each line is emitted exactly once: # # ['foo/bar/baz.cpp', 'foo/bar', 0], # ['foo/bar', 'foo', 0], # ['foo', 'ROOT', 0], def add_path(path): if not path in parent_map: head = os.path.split(path)[0] if not head: parent_map[path] = "ROOT" else: add_path(head) parent_map[path] = head # We add a suffix to paths to eliminate the chances of a path name colliding with a symbol # name. This is important because google.visualization.TreeMap requires node names to be # unique, and a file such as test/foo/bar.cpp would create a node named "test", which could # collide with a symbol named "test" defined in a C++ file. # # Assumptions made: # - No C++ symbol ends with " (Path)". # - No C++ symbol is named "ROOT". parent = parent_map[path] if parent != "ROOT": parent = "%s (Path)" % parent print("['%s (Path)', '%s', 0]," % (path, parent)) def main(): # HTML/script header, plus the first two (fixed) rows of the data table print("""
""") if __name__ == "__main__": main()