Skip to content

Commit

Permalink
Review comments. Mostly python improvements in symbolicate-linux-fata…
Browse files Browse the repository at this point in the history
…l(.py).
  • Loading branch information
gmilos committed Sep 7, 2016
1 parent f068b1e commit dc3369d
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 121 deletions.
6 changes: 4 additions & 2 deletions stdlib/public/runtime/Errors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,6 @@ static bool getSymbolNameAddr(llvm::StringRef libraryName, Dl_info dlinfo,
// need to use the hex address.
bool hasUnavailableAddress = dlinfo.dli_sname == nullptr;

// If the address is unavailable, just use <unavailable> as the symbol
// name. We do not set addrOut, since addrOut will be set to our ptr address.
if (hasUnavailableAddress) {
return false;
}
Expand Down Expand Up @@ -131,6 +129,10 @@ static void dumpStackTraceEntry(unsigned index, void *framePC) {
// We do not use %p here for our pointers since the format is implementation
// defined. This makes it logically impossible to check the output. Forcing
// hexadecimal solves this issue.
// If the symbol is not available, we print out <unavailable> + offset
// from the base address of where the image containing framePC is mapped.
// This gives enough info to reconstruct identical debugging target after
// this process terminates.
if (foundSymbol) {
static const char *backtraceEntryFormat = "%-4u %-34s 0x%0.16lx %s + %td\n";
fprintf(stderr, backtraceEntryFormat, index, libraryName.data(), symbolAddr,
Expand Down
158 changes: 158 additions & 0 deletions utils/symbolicate-linux-fatal
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()
119 changes: 0 additions & 119 deletions utils/symbolicate-linux-fatal.py

This file was deleted.

0 comments on commit dc3369d

Please sign in to comment.