xref: /aosp_15_r20/external/perfetto/tools/symbolize-ui-crash (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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