blob: 59e1e39e08a0ac5f0e8e7a23a2137af1cc4d06df [file] [log] [blame]
Kevin Lubicka9246dd2017-02-15 10:20:30 -05001#!/usr/bin/env python
2# Copyright 2017 The Chromium Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5# pylint: disable=line-too-long
6
Kevin Lubick0548a212017-02-20 08:48:35 -05007import collections
Kevin Lubicka9246dd2017-02-15 10:20:30 -05008import os
9import re
10import subprocess
11import sys
12
13# Run a command and symbolize anything that looks like a stacktrace in the
14# stdout/stderr. This will return with the same error code as the command.
15
16# First parameter is the current working directory, which will be stripped
17# out of stacktraces. The rest of the parameters will be fed to
18# subprocess.check_output() and should be the command and arguments that
19# will be fed in. If any environment variables are set when running this
20# script, they will be automatically used by the call to
21# subprocess.check_output().
22
23# This wrapper function is needed to make sure stdout and stderr stay properly
24# interleaved, to assist in debugging. There are no clean ways to achieve
25# this with recipes. For example, running the dm step with parameters like
26# stdout=api.raw_io.output(), stderr=api.raw_io.output() ended up with
27# stderr and stdout being separate files, which eliminated the interwoven logs.
28# Aside from specifying stdout/stderr, there are no ways to capture or reason
29# about the logs of previous steps without using a wrapper like this.
30
31def main(basedir, cmd):
Kevin Lubick0548a212017-02-20 08:48:35 -050032 logs = collections.deque(maxlen=200)
Kevin Lubicka9246dd2017-02-15 10:20:30 -050033
Kevin Lubick0548a212017-02-20 08:48:35 -050034 proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
35 stderr=subprocess.STDOUT)
36 for line in iter(proc.stdout.readline, ''):
37 sys.stdout.write(line)
38 logs.append(line)
39 proc.wait()
40 print 'Command exited with code %s' % proc.returncode
Kevin Lubicka9246dd2017-02-15 10:20:30 -050041 # Stacktraces generally look like:
42 # /lib/x86_64-linux-gnu/libc.so.6(abort+0x16a) [0x7fa90e8d0c62]
43 # /b/s/w/irISUIyA/linux_vulkan_intel_driver_debug/./libvulkan_intel.so(+0x1f4d0a) [0x7fa909eead0a]
44 # /b/s/w/irISUIyA/out/Debug/dm() [0x17c3c5f]
45 # The stack_line regex splits those into three parts. Experimentation has
46 # shown that the address in () works best for external libraries, but our code
47 # doesn't have that. So, we capture both addresses and prefer using the first
48 # over the second, unless the first is blank or invalid. Relative offsets
49 # like abort+0x16a are ignored.
50 stack_line = r'^(?P<path>.+)\(\+?(?P<addr>.*)\) \[(?P<addr2>.+)\]'
51 # After performing addr2line, the result can be something obnoxious like:
52 # foo(bar) at /b/s/w/a39kd/Skia/out/Clang/../../src/gpu/Frobulator.cpp:13
53 # The extra_path strips off the not-useful prefix and leaves just the
54 # important src/gpu/Frobulator.cpp:13 bit.
55 extra_path = r'/.*\.\./'
Kevin Lubick0548a212017-02-20 08:48:35 -050056 is_first = True
57 for line in logs:
Kevin Lubicka9246dd2017-02-15 10:20:30 -050058 line = line.strip()
59
60 m = re.search(stack_line, line)
61 if m:
Kevin Lubick0548a212017-02-20 08:48:35 -050062 if is_first:
63 print '#######################################'
64 print 'symbolized stacktrace follows'
65 print '#######################################'
66 is_first = False
67
Kevin Lubicka9246dd2017-02-15 10:20:30 -050068 path = m.group('path')
69 addr = m.group('addr')
70 addr2 = m.group('addr2')
71 if os.path.exists(path):
72 if not addr or not addr.startswith('0x'):
73 addr = addr2
74 sym = subprocess.check_output(['addr2line', '-Cfpe', path, addr])
75 sym = sym.strip()
76 # If addr2line doesn't return anything useful, we don't replace the
77 # original address, so the human can see it.
78 if sym and not sym.startswith('?'):
79 if path.startswith(basedir):
80 path = path[len(basedir)+1:]
81 sym = re.sub(extra_path, '', sym)
82 line = path + ' ' + sym
Kevin Lubick0548a212017-02-20 08:48:35 -050083 print line
Kevin Lubicka9246dd2017-02-15 10:20:30 -050084
Kevin Lubick0548a212017-02-20 08:48:35 -050085 sys.exit(proc.returncode)
Kevin Lubicka9246dd2017-02-15 10:20:30 -050086
87
88if __name__ == '__main__':
89 if len(sys.argv) < 3:
90 print >> sys.stderr, 'USAGE: %s working_dir cmd_and_args...' % sys.argv[0]
91 sys.exit(1)
92 main(sys.argv[1], sys.argv[2:])