forked from swiftlang/swift
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Review comments. Mostly python improvements in symbolicate-linux-fata…
…l(.py).
- Loading branch information
Showing
3 changed files
with
162 additions
and
121 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
#!/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 - 2016 Apple Inc. and the Swift project authors | ||
# Licensed under Apache License v2.0 with Runtime Library Exception | ||
# | ||
# See http://swift.org/LICENSE.txt for license information | ||
# See http://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 analysed 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 sys | ||
import lldb | ||
import subprocess | ||
|
||
DYN_LIBS = {} | ||
BINARY = None | ||
|
||
|
||
def process_ldd(lddoutput): | ||
for line in lddoutput.splitlines(): | ||
ldd_tokens = line.split() | ||
if len(ldd_tokens) >= 3: | ||
DYN_LIBS[ldd_tokens[0]] = ldd_tokens[2] | ||
|
||
|
||
def create_lldb_target(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(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] | ||
else: | ||
if 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: | ||
print("Mismatched base address for: {0:s}, " | ||
"had: {1:x}, now got {2:x}".format( | ||
dynlib_path, memmap[dynlib_path], dynlib_baseaddr)) | ||
sys.exit(1) | ||
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}) | ||
|
||
lldb_target = create_lldb_target(memmap) | ||
frame_idx = 0 | ||
for frame in full_stack: | ||
use_orig_line = True | ||
frame_addr = frame["framePC"] | ||
so_addr = lldb_target.ResolveLoadAddress(frame_addr-1) | ||
sym_ctx = so_addr.GetSymbolContext(lldb.eSymbolContextEverything) | ||
frame_fragment = "{0: <4d} 0x{1:016x}".format(frame_idx, 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(): | ||
global BINARY | ||
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", help="Log file containing the stack trace to symbolicate") | ||
args = parser.parse_args() | ||
|
||
BINARY = args.binary | ||
logfile = args.log | ||
|
||
lddoutput = subprocess.check_output( | ||
['ldd', BINARY], stderr=subprocess.STDOUT) | ||
process_ldd(lddoutput) | ||
|
||
instack = False | ||
stackidx = 0 | ||
stack = [] | ||
with open(logfile, 'rU') as f: | ||
for line in f: | ||
if instack and line.startswith(str(stackidx)): | ||
stack.append(line) | ||
stackidx = stackidx + 1 | ||
else: | ||
instack = False | ||
stackidx = 0 | ||
process_stack(stack) | ||
stack = [] | ||
print(line.rstrip()) | ||
if line.startswith("Current stack trace:"): | ||
instack = True | ||
process_stack(stack) | ||
|
||
|
||
if __name__ == '__main__': | ||
main() |
This file was deleted.
Oops, something went wrong.