1*6dbdd20aSAndroid Build Coastguard Worker#!/usr/bin/env python3 2*6dbdd20aSAndroid Build Coastguard Worker# Copyright (C) 2017 The Android Open Source Project 3*6dbdd20aSAndroid Build Coastguard Worker# 4*6dbdd20aSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 5*6dbdd20aSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 6*6dbdd20aSAndroid Build Coastguard Worker# You may obtain a copy of the License at 7*6dbdd20aSAndroid Build Coastguard Worker# 8*6dbdd20aSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 9*6dbdd20aSAndroid Build Coastguard Worker# 10*6dbdd20aSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 11*6dbdd20aSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 12*6dbdd20aSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*6dbdd20aSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 14*6dbdd20aSAndroid Build Coastguard Worker# limitations under the License. 15*6dbdd20aSAndroid Build Coastguard Worker"""Like llvm-symbolizer for UI JS/TS sources. 16*6dbdd20aSAndroid Build Coastguard Worker 17*6dbdd20aSAndroid Build Coastguard WorkerThis script is used to "symbolize" UI crashes. It takes a crash, typically 18*6dbdd20aSAndroid Build Coastguard Workercopied from a bug reports, of the form: 19*6dbdd20aSAndroid Build Coastguard Worker 20*6dbdd20aSAndroid Build Coastguard Worker(https://ui.perfetto.dev/v12.1.269/frontend_bundle.js:7639:61) at foo() 21*6dbdd20aSAndroid Build Coastguard Worker(https://ui.perfetto.dev/v12.1.269/frontend_bundle.js:9235:29) at bar() 22*6dbdd20aSAndroid Build Coastguard Worker 23*6dbdd20aSAndroid Build Coastguard Workerit fetches the corresponding source maps and emits in output a translated 24*6dbdd20aSAndroid Build Coastguard Workercrash report, of the form: 25*6dbdd20aSAndroid Build Coastguard Worker 26*6dbdd20aSAndroid Build Coastguard Workerhttps://android.googlesource.com/platform/external/perfetto/+/de4db33f/ui/src/foo.ts#61 at foo() 27*6dbdd20aSAndroid Build Coastguard Workerhttps://android.googlesource.com/platform/external/perfetto/+/de4db33f/ui/src/baz.ts#300 at bar() 28*6dbdd20aSAndroid Build Coastguard Worker""" 29*6dbdd20aSAndroid Build Coastguard Worker 30*6dbdd20aSAndroid Build Coastguard Workerimport logging 31*6dbdd20aSAndroid Build Coastguard Workerimport re 32*6dbdd20aSAndroid Build Coastguard Workerimport sys 33*6dbdd20aSAndroid Build Coastguard Workerimport tempfile 34*6dbdd20aSAndroid Build Coastguard Workerimport urllib.request 35*6dbdd20aSAndroid Build Coastguard Workerimport ssl 36*6dbdd20aSAndroid Build Coastguard Workerimport os 37*6dbdd20aSAndroid Build Coastguard Worker 38*6dbdd20aSAndroid Build Coastguard Workertry: 39*6dbdd20aSAndroid Build Coastguard Worker import sourcemap 40*6dbdd20aSAndroid Build Coastguard Workerexcept: 41*6dbdd20aSAndroid Build Coastguard Worker print('Run `pip3 install sourcemap` and try again') 42*6dbdd20aSAndroid Build Coastguard Worker sys.exit(1) 43*6dbdd20aSAndroid Build Coastguard Worker 44*6dbdd20aSAndroid Build Coastguard WorkerGERRIT_BASE_URL = 'https://android.googlesource.com/platform/external/perfetto/' 45*6dbdd20aSAndroid Build Coastguard Worker 46*6dbdd20aSAndroid Build Coastguard Worker 47*6dbdd20aSAndroid Build Coastguard Workerdef fetch_url_cached(url): 48*6dbdd20aSAndroid Build Coastguard Worker normalized = re.sub('[^a-zA-Z0-9-._]', '_', url) 49*6dbdd20aSAndroid Build Coastguard Worker local_file = os.path.join(tempfile.gettempdir(), normalized) 50*6dbdd20aSAndroid Build Coastguard Worker if os.path.exists(local_file): 51*6dbdd20aSAndroid Build Coastguard Worker logging.debug('Using %s', local_file) 52*6dbdd20aSAndroid Build Coastguard Worker with open(local_file, 'r') as f: 53*6dbdd20aSAndroid Build Coastguard Worker return f.read() 54*6dbdd20aSAndroid Build Coastguard Worker context = ssl._create_unverified_context() 55*6dbdd20aSAndroid Build Coastguard Worker logging.info('Fetching %s', url) 56*6dbdd20aSAndroid Build Coastguard Worker resp = urllib.request.urlopen(url, context=context) 57*6dbdd20aSAndroid Build Coastguard Worker contents = resp.read().decode() 58*6dbdd20aSAndroid Build Coastguard Worker with open(local_file, 'w') as f: 59*6dbdd20aSAndroid Build Coastguard Worker f.write(contents) 60*6dbdd20aSAndroid Build Coastguard Worker return contents 61*6dbdd20aSAndroid Build Coastguard Worker 62*6dbdd20aSAndroid Build Coastguard Worker 63*6dbdd20aSAndroid Build Coastguard Workerdef Main(): 64*6dbdd20aSAndroid Build Coastguard Worker if len(sys.argv) > 1: 65*6dbdd20aSAndroid Build Coastguard Worker with open(sys.argv[1], 'r') as f: 66*6dbdd20aSAndroid Build Coastguard Worker txt = f.read() 67*6dbdd20aSAndroid Build Coastguard Worker else: 68*6dbdd20aSAndroid Build Coastguard Worker if sys.stdin.isatty(): 69*6dbdd20aSAndroid Build Coastguard Worker print('Paste the crash log and press CTRL-D\n') 70*6dbdd20aSAndroid Build Coastguard Worker txt = sys.stdin.read() 71*6dbdd20aSAndroid Build Coastguard Worker 72*6dbdd20aSAndroid Build Coastguard Worker # Look for the GIT commitish appended in crash reports. This is not required 73*6dbdd20aSAndroid Build Coastguard Worker # for resolving the sourcemaps but helps generating better links. 74*6dbdd20aSAndroid Build Coastguard Worker matches = re.findall(r'https://ui.perfetto.dev/(.*-)([a-f0-9]{6,})\n', txt) 75*6dbdd20aSAndroid Build Coastguard Worker if not matches: 76*6dbdd20aSAndroid Build Coastguard Worker logging.fatal('Could not determine the version.' 77*6dbdd20aSAndroid Build Coastguard Worker 'The crash report should have a line like: ' 78*6dbdd20aSAndroid Build Coastguard Worker '"UI: https://ui.perfetto.dev/v12.3-abcdef"') 79*6dbdd20aSAndroid Build Coastguard Worker return 1 80*6dbdd20aSAndroid Build Coastguard Worker 81*6dbdd20aSAndroid Build Coastguard Worker dir_name = matches[0][0] + matches[0][1] 82*6dbdd20aSAndroid Build Coastguard Worker git_rev = matches[0][1] 83*6dbdd20aSAndroid Build Coastguard Worker base_url = 'https://commondatastorage.googleapis.com/ui.perfetto.dev/' + dir_name + '/' 84*6dbdd20aSAndroid Build Coastguard Worker matches = re.findall(r'(\((.+[.]js):(\d+):(\d+)\))', txt) 85*6dbdd20aSAndroid Build Coastguard Worker maps_by_url = {} 86*6dbdd20aSAndroid Build Coastguard Worker sym_lines = '' 87*6dbdd20aSAndroid Build Coastguard Worker for entry in matches: 88*6dbdd20aSAndroid Build Coastguard Worker whole_token, script_url, line, col = entry 89*6dbdd20aSAndroid Build Coastguard Worker if '/' not in script_url: 90*6dbdd20aSAndroid Build Coastguard Worker script_url = base_url + script_url 91*6dbdd20aSAndroid Build Coastguard Worker map_url = script_url + '.map' 92*6dbdd20aSAndroid Build Coastguard Worker if map_url in maps_by_url: 93*6dbdd20aSAndroid Build Coastguard Worker srcmap = maps_by_url[map_url] 94*6dbdd20aSAndroid Build Coastguard Worker else: 95*6dbdd20aSAndroid Build Coastguard Worker map_file_contents = fetch_url_cached(map_url) 96*6dbdd20aSAndroid Build Coastguard Worker srcmap = sourcemap.loads(map_file_contents) 97*6dbdd20aSAndroid Build Coastguard Worker maps_by_url[map_url] = srcmap 98*6dbdd20aSAndroid Build Coastguard Worker sym = srcmap.lookup(int(line), int(col)) 99*6dbdd20aSAndroid Build Coastguard Worker src = sym.src.replace('../../', '') 100*6dbdd20aSAndroid Build Coastguard Worker sym_url = '%s#%s' % (src, sym.src_line) 101*6dbdd20aSAndroid Build Coastguard Worker if src.startswith('../out/ui/'): 102*6dbdd20aSAndroid Build Coastguard Worker src = src.replace('../out/ui/', 'ui/') 103*6dbdd20aSAndroid Build Coastguard Worker sym_url = GERRIT_BASE_URL + '/+/%s/%s#%d' % (git_rev, src, sym.src_line) 104*6dbdd20aSAndroid Build Coastguard Worker sym_lines += sym_url + '\n' 105*6dbdd20aSAndroid Build Coastguard Worker txt = txt.replace(whole_token, sym_url) 106*6dbdd20aSAndroid Build Coastguard Worker 107*6dbdd20aSAndroid Build Coastguard Worker print(txt) 108*6dbdd20aSAndroid Build Coastguard Worker print('\nResolved symbols:\n' + sym_lines) 109*6dbdd20aSAndroid Build Coastguard Worker 110*6dbdd20aSAndroid Build Coastguard Worker 111*6dbdd20aSAndroid Build Coastguard Workerif __name__ == '__main__': 112*6dbdd20aSAndroid Build Coastguard Worker logging.basicConfig(level=logging.INFO) 113*6dbdd20aSAndroid Build Coastguard Worker sys.exit(Main()) 114