Files
swift-mirror/utils/symbolicate-linux-fatal
Hugh Bellamy ad4b338062 Fix python lint failures now not excluded as we provide custom exclusions
Looks like flake8 enables other rules when you add something to the
exclusion list. We added W291
2017-03-27 12:31:56 +07:00

159 lines
5.5 KiB
Python
Executable File

#!/usr/bin/env python
# symbolicate-linux-fatal - Symbolicate Linux stack traces -*- python -*-
#
# This source file is part of the Swift.org open source project
#
# Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
# Licensed under Apache License v2.0 with Runtime Library Exception
#
# See https://swift.org/LICENSE.txt for license information
# See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
#
# ----------------------------------------------------------------------------
#
# Symbolicates fatalError stack traces on Linux. Takes the main binary
# and a log file containing a stack trace. Non-stacktrace lines are output
# unmodified. Stack trace elements are analyzed using reconstructed debug
# target matching the original process in where shared libs where mapped.
#
# TODOs:
# * verbose output
# * search symbols by name for the not <unavailable> ones
#
# ----------------------------------------------------------------------------
from __future__ import print_function
import argparse
import subprocess
import lldb
def process_ldd(lddoutput):
dyn_libs = {}
for line in lddoutput.splitlines():
ldd_tokens = line.split()
if len(ldd_tokens) >= 3:
dyn_libs[ldd_tokens[0]] = ldd_tokens[2]
return dyn_libs
def create_lldb_target(binary, memmap):
lldb_debugger = lldb.SBDebugger.Create()
lldb_target = lldb_debugger.CreateTargetWithFileAndArch(
binary, lldb.LLDB_ARCH_DEFAULT)
module = lldb_target.GetModuleAtIndex(0)
# lldb seems to treat main binary differently, slide offset must be zero
lldb_target.SetModuleLoadAddress(module, 0)
for dynlib_path in memmap:
if binary not in dynlib_path:
module = lldb_target.AddModule(
dynlib_path, lldb.LLDB_ARCH_DEFAULT, None, None)
lldb_target.SetModuleLoadAddress(module, memmap[dynlib_path])
return lldb_target
def process_stack(binary, dyn_libs, stack):
if len(stack) == 0:
return
memmap = {}
full_stack = []
for line in stack:
stack_tokens = line.split()
dynlib_fname = stack_tokens[1]
if dynlib_fname in dyn_libs:
dynlib_path = dyn_libs[dynlib_fname]
elif dynlib_fname in binary:
dynlib_path = binary
else:
dynlib_path = None
if "<unavailable>" in stack_tokens[3]:
framePC = int(stack_tokens[2], 16)
symbol_offset = int(stack_tokens[-1], 10)
dynlib_baseaddr = framePC - symbol_offset
if dynlib_path in memmap:
if memmap[dynlib_path] != dynlib_baseaddr:
error_msg = "Mismatched base address for: {0:s}, " \
"had: {1:x}, now got {2:x}"
error_msg = error_msg.format(
dynlib_path, memmap[dynlib_path], dynlib_baseaddr)
raise Exception(error_msg)
else:
memmap[dynlib_path] = dynlib_baseaddr
else:
framePC = int(stack_tokens[2], 16) + int(stack_tokens[-1], 10)
full_stack.append(
{"line": line, "framePC": framePC, "dynlib_fname": dynlib_fname})
lldb_target = create_lldb_target(binary, memmap)
frame_idx = 0
for frame in full_stack:
use_orig_line = True
frame_addr = frame["framePC"]
dynlib_fname = frame["dynlib_fname"]
so_addr = lldb_target.ResolveLoadAddress(frame_addr - 1)
sym_ctx = so_addr.GetSymbolContext(lldb.eSymbolContextEverything)
frame_fragment = "{0: <4d} {1:20s} 0x{2:016x}".format(
frame_idx, dynlib_fname, frame_addr)
symbol = sym_ctx.GetSymbol()
if symbol.IsValid():
symbol_base = symbol.GetStartAddress().GetLoadAddress(lldb_target)
symbol_fragment = "{0:s} + {1:d}".format(
symbol.GetName(), frame_addr - symbol_base)
use_orig_line = False
else:
symbol_fragment = "<unavailable>"
line_entry = sym_ctx.GetLineEntry()
if line_entry.IsValid():
line_fragment = "at {0:s}:{1:d}".format(
line_entry.GetFileSpec().GetFilename(), line_entry.GetLine())
else:
line_fragment = ""
if use_orig_line:
print(frame["line"].rstrip())
else:
print("{0:s} {1:s} {2:s}".format(
frame_fragment, symbol_fragment, line_fragment))
frame_idx = frame_idx + 1
def main():
parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
description="""Symbolicates stack traces in Linux log files.""")
parser.add_argument(
"binary", help="Executable which produced the log file")
parser.add_argument(
"log", type=argparse.FileType("rU"),
help="Log file containing the stack trace to symbolicate")
args = parser.parse_args()
binary = args.binary
lddoutput = subprocess.check_output(
['ldd', binary], stderr=subprocess.STDOUT)
dyn_libs = process_ldd(lddoutput)
instack = False
stackidx = 0
stack = []
for line in args.log:
if instack and line.startswith(str(stackidx)):
stack.append(line)
stackidx = stackidx + 1
else:
instack = False
stackidx = 0
process_stack(binary, dyn_libs, stack)
stack = []
print(line.rstrip())
if line.startswith("Current stack trace:"):
instack = True
process_stack(binary, dyn_libs, stack)
if __name__ == '__main__':
main()