diff options
Diffstat (limited to 'chromium/tools/win/trace-sandbox-viewer.py')
-rw-r--r-- | chromium/tools/win/trace-sandbox-viewer.py | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/chromium/tools/win/trace-sandbox-viewer.py b/chromium/tools/win/trace-sandbox-viewer.py new file mode 100644 index 00000000000..eb1420a332f --- /dev/null +++ b/chromium/tools/win/trace-sandbox-viewer.py @@ -0,0 +1,212 @@ +# Copyright 2020 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. +"""Use Chromium tracing to associate mojo endpoints with sandboxes. + +This script provides a rich view of where mojo interfaces are hosted +and consumed when Chromium is build with mojo tracing enabled, and +provides a coarser view for default builds or packages such as Chrome +Canary. + +## With Mojo Tracing Enabled + +1. Enable detailed mojo tracing: + +gn args ./out/Default +... +enable_mojo_tracing = true +... +autoninja -C ./out/Default chrome + +2. Collect a trace: + +chrome.exe --trace-startup=-*,disabled-by-default-sandbox,startup,mojom ^ + --trace-startup-file=c:/src/temp/tracing/foo.log ^ + --trace-startup-duration=0 + +Or visit chrome://tracing and enable categories above, save file. + +3. Run this script: + +trace-sandbox-viewer.py file.log + +trace-sandbox-viewer.py file.json.gz + +### Output + +{ + "None(Browser)": [ + "(Impl)IPC::mojom::Channel::GetAssociatedInterface", + "(Impl)IPC::mojom::Channel::Receive", + "(Impl)IPC::mojom::Channel::SetPeerPid", + "(Impl)apps::mojom::AppService::RegisterPublisher", + "(Impl)apps::mojom::AppService::RegisterSubscriber", + "(Impl)apps::mojom::Publisher::Connect", + ... + "data_decoder::mojom::DataDecoderService::BindJsonParser", + "data_decoder::mojom::JsonParser::Parse", + "data_decoder::mojom::JsonParser::ParseCallback", + ... + ], + "Network": [ + ... + ], + "Renderer": [ + "(Impl)IPC::mojom::Channel::GetAssociatedInterface", + "(Impl)IPC::mojom::Channel::Receive", + "(Impl)IPC::mojom::Channel::SetPeerPid", + "(Impl)autofill::mojom::AutofillAgent::FieldTypePredictionsAvailable", + "(Impl)autofill::mojom::PasswordAutofillAgent::SetLoggingState", + "(Impl)blink::mojom::AppCacheFrontend::CacheSelected", + ... + ], + "Utility": [ + "(Impl)IPC::mojom::Channel::SetPeerPid", + "(Impl)content::mojom::ChildHistogramFetcherFactory::CreateFetcher", + "(Impl)content::mojom::ChildProcess::BindReceiver", + "(Impl)content::mojom::ChildProcess::BindServiceInterface", + "(Impl)content::mojom::ChildProcess::BootstrapLegacyIpc", + "(Impl)content::mojom::ChildProcess::GetBackgroundTracingAgentProvider", + "(Impl)content::mojom::ChildProcess::Initialize", + "(Impl)data_decoder::mojom::DataDecoderService::BindImageDecoder", + "(Impl)data_decoder::mojom::DataDecoderService::BindJsonParser", + "(Impl)data_decoder::mojom::ImageDecoder::DecodeImage", + "(Impl)data_decoder::mojom::ImageDecoder::DecodeImageCallback", + "(Impl)data_decoder::mojom::JsonParser::Parse", + "(Impl)data_decoder::mojom::JsonParser::ParseCallback", + ... + ], +} +``` + +## Default Builds + +This script is also useful for default builds of Chromium but will +show interfaces in both hosting and consuming processes (as mojom +tracing is limited). Run using the same arguments as above. + +### Output. + +Note interfaces appear in both host and client contexts: + +``` +{ + "None(Browser)": [ + "IPC Channel", + "apps.mojom.AppService", + "apps.mojom.Publisher", + ... + "data_decoder.mojom.DataDecoderService", + "data_decoder.mojom.JsonParser", + ... + ], + "Utility": [ + "IPC Channel", + "content.mojom.ChildHistogramFetcherFactory", + "content.mojom.ChildProcess", + "data_decoder.mojom.DataDecoderService", + "data_decoder.mojom.JsonParser", + ... + ], + ... + "Renderer": [ + "IPC Channel", + "blink.mojom.AppCacheFrontend", + ... + "network.mojom.URLLoaderClient", + "safe_browsing.mojom.PhishingModelSetter", + "safe_browsing.mojom.SafeBrowsing", + ... + ], + ... +} +``` + +""" + +import argparse +import gzip +import json +import sys + + +def guess_open_file(filename): + if filename.endswith('.gz'): + return gzip.open(filename, mode='rb') + else: + return open(filename, 'rb') + + +def read_json_events(files, categories): + events = [] + for filename in files: + with guess_open_file(filename) as fp: + objs = json.load(fp) + for event in objs['traceEvents']: + if event['cat'] in categories: + events.append(event) + return events + + +def assign_interfaces_to_sandboxes(events): + # Returns SandboxType:set(Interfaces). + intmap = {} + # Running map of PID:SandboxType + pidmap = {} + # running map of PID:ProcName + procmap = {} + # Assumes events are sorted by timestamp. + for event in events: + # Don't yet know how to remove processes when they finish + # (i.e. which event to match). + if event['cat'] == 'mojom': + pid = event['pid'] + interface = event['name'] + # If there is a sandbox, use that. + if pid in pidmap: + sbox = pidmap[pid] + if not sbox in intmap: + intmap[sbox] = set() + intmap[sbox].add(interface) + # Otherwise if we saw a process, use that. + elif pid in procmap: + sbox = "None(" + procmap[pid] + ")" + if not sbox in intmap: + intmap[sbox] = set() + intmap[sbox].add(interface) + elif event['cat'] == '__metadata' and event['name'] == 'process_name': + pid = event['pid'] + proc = event['args']['name'] + procmap[pid] = proc + elif event['cat'] == 'disabled-by-default-sandbox': + pid = int(event['args']['policy']['processIds'][0]) + sbox = event['args']['sandboxType'] + pidmap[pid] = sbox + return intmap + + +def output_as_json(interfaces): + dumpable = {} + for host in interfaces: + dumpable[host] = sorted(interfaces[host]) + return json.dumps(dumpable, indent=2) + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument('files', nargs='*', help='file file...') + args = parser.parse_args() + if len(args.files) < 1: + print("Need at least one file") + return 1 + + events = read_json_events( + args.files, + categories=['mojom', 'disabled-by-default-sandbox', '__metadata']) + interfaces = assign_interfaces_to_sandboxes(events) + + print(output_as_json(interfaces)) + + +if __name__ == '__main__': + sys.exit(main()) |