| #!/usr/bin/env python3.8 | |
| # Copyright 2020 The Fuchsia Authors. All rights reserved. | |
| # Use of this source code is governed by a BSD-style license that can be | |
| # found in the LICENSE file. | |
| #### CATEGORY=Run, inspect and debug | |
| ### Extract pprof data from inspect.json | |
| ## | |
| ## inspect.json holds Inspect data generated as part of a snapshot (see fx snapshot). | |
| ## pprof is a tool used to analyze profiling data (for more details, see | |
| ## https://github.com/google/pprof). Some components are configured to provide | |
| ## the Inspect tool with pprof data that can later be used to analyze the running | |
| ## program's state (memory, threads, etc.). This is especially useful after a | |
| ## crash where developers can observe the last known state before the crash. | |
| ## | |
| ## This tool extracts the pprof data for some component from inspect.json and | |
| ## decodes it from its stored format (base64 encoded) into the format the pprof | |
| ## tool will understand (raw binary). The pprof files will be extracted to a | |
| ## temporary directory. | |
| ## | |
| ## usage: fx extract_pprof --inspect FILE --component CMX | |
| ## | |
| ## --inspect Path to the inspect.json file | |
| ## --component Component to extract pprof data for (i.e. netstack.cmx) | |
| import argparse | |
| import base64 | |
| import json | |
| import os | |
| import sys | |
| import tempfile | |
| def main(): | |
| parser = argparse.ArgumentParser( | |
| description="Extract pprof data from inspect.json") | |
| parser.add_argument( | |
| "--inspect", required=True, help="Path to inspect.json file") | |
| parser.add_argument( | |
| "--component", | |
| required=True, | |
| help="Component to extract pprof data for (i.e. netstack.cmx)") | |
| args = parser.parse_args() | |
| tempdir = tempfile.mkdtemp() | |
| print("inspect.json @ %s" % (args.inspect)) | |
| print("component = %s" % (args.component)) | |
| print("output @ %s" % (tempdir)) | |
| with open(args.inspect, "r") as f: | |
| inspect = json.loads(f.read()) | |
| for entry in inspect: | |
| if entry["data_source"] != "Inspect" or entry["moniker"] != args.component: | |
| continue | |
| filepath = entry["metadata"]["filename"] | |
| # We only care about pprof data. | |
| if not filepath.startswith("pprof"): | |
| continue | |
| filepath = os.path.join(tempdir, filepath) | |
| os.makedirs(filepath, exist_ok=True) | |
| data = entry["payload"]["root"]["pprof"] | |
| for k, v in data.items(): | |
| # The first 4 characters are used to indicate that the file is a | |
| # base64 encoded file. | |
| prefix, v = v[:4], v[4:] | |
| if prefix != "b64:": | |
| print("%s at %s is not base64 encoded" % (k, filepath)) | |
| with open(os.path.join(filepath, k), "wb") as f: | |
| f.write(base64.b64decode(v)) | |
| if __name__ == "__main__": | |
| main() |