Skip to content

[Runtime] Add an option to produce non-symbolicated backtraces. #71386

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Feb 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/Backtracing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ follows:
| | | standard error instead of standard output. This |
| | | may be useful in some CI systems. |
+-----------------+---------+--------------------------------------------------+
| symbolicate | full | Options are ``full``, ``fast``, or ``off``. |
| | | Full means to look up source locations and |
| | | inline frames. Fast just does symbol lookup. |
+-----------------+---------+--------------------------------------------------+
| swift-backtrace | | If specified, gives the full path to the |
| | | swift-backtrace binary to use for crashes. |
| | | Otherwise, Swift will locate the binary relative |
Expand Down
1 change: 1 addition & 0 deletions docs/contents.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Contents
.. toctree::
:maxdepth: 1

Backtracing
Generics
StoredAndComputedVariables
SIL
Expand Down
17 changes: 12 additions & 5 deletions stdlib/public/Backtracing/Backtrace.swift
Original file line number Diff line number Diff line change
Expand Up @@ -613,20 +613,27 @@ public struct Backtrace: CustomStringConvertible, Sendable {
/// running on, add virtual frames to show inline
/// function calls.
///
/// @param showSourceLocation If `true`, look up the source location for
/// each address.
///
/// @param useSymbolCache If the system we are on has a symbol cache,
/// says whether or not to use it.
///
/// @returns A new `SymbolicatedBacktrace`.
public func symbolicated(with images: [Image]? = nil,
sharedCacheInfo: SharedCacheInfo? = nil,
showInlineFrames: Bool = true,
showSourceLocations: Bool = true,
useSymbolCache: Bool = true)
-> SymbolicatedBacktrace? {
return SymbolicatedBacktrace.symbolicate(backtrace: self,
images: images,
sharedCacheInfo: sharedCacheInfo,
showInlineFrames: showInlineFrames,
useSymbolCache: useSymbolCache)
return SymbolicatedBacktrace.symbolicate(
backtrace: self,
images: images,
sharedCacheInfo: sharedCacheInfo,
showInlineFrames: showInlineFrames,
showSourceLocations: showSourceLocations,
useSymbolCache: useSymbolCache
)
}

/// Provide a textual version of the backtrace.
Expand Down
2 changes: 1 addition & 1 deletion stdlib/public/Backtracing/BacktraceFormatter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -545,12 +545,12 @@ public struct BacktraceFormatter {
if let index = index {
columns.append(options._theme.frameIndex("\(index)"))
}
columns.append(options._theme.programCounter(pc))
if options._showFrameAttributes {
columns.append(attrs.map(
options._theme.frameAttribute
).joined(separator: " "))
}
columns.append(options._theme.programCounter(pc))

return columns
}
Expand Down
88 changes: 57 additions & 31 deletions stdlib/public/Backtracing/SymbolicatedBacktrace.swift
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,7 @@ public struct SymbolicatedBacktrace: CustomStringConvertible {
with owner: CSSymbolOwnerRef,
isInline: Bool,
symbol: CSSymbolRef,
sourceInfo: CSSourceInfoRef,
sourceInfo: CSSourceInfoRef?,
images: [Backtrace.Image]) -> Frame {
if CSIsNull(symbol) {
return Frame(captured: capturedFrame, symbol: nil)
Expand All @@ -354,7 +354,7 @@ public struct SymbolicatedBacktrace: CustomStringConvertible {

let location: SourceLocation?

if !CSIsNull(sourceInfo) {
if let sourceInfo = sourceInfo, !CSIsNull(sourceInfo) {
let path = CSSourceInfoGetPath(sourceInfo) ?? "<unknown>"
let line = CSSourceInfoGetLineNumber(sourceInfo)
let column = CSSourceInfoGetColumn(sourceInfo)
Expand Down Expand Up @@ -395,6 +395,7 @@ public struct SymbolicatedBacktrace: CustomStringConvertible {
images: [Backtrace.Image]?,
sharedCacheInfo: Backtrace.SharedCacheInfo?,
showInlineFrames: Bool,
showSourceLocations: Bool,
useSymbolCache: Bool)
-> SymbolicatedBacktrace? {

Expand Down Expand Up @@ -454,7 +455,7 @@ public struct SymbolicatedBacktrace: CustomStringConvertible {

first = false
}
} else {
} else if showSourceLocations {
let symbol = CSSymbolOwnerGetSymbolWithAddress(owner, address)
let sourceInfo = CSSymbolOwnerGetSourceInfoWithAddress(owner,
address)
Expand All @@ -465,6 +466,15 @@ public struct SymbolicatedBacktrace: CustomStringConvertible {
symbol: symbol,
sourceInfo: sourceInfo,
images: theImages))
} else {
let symbol = CSSymbolOwnerGetSymbolWithAddress(owner, address)

frames.append(buildFrame(from: frame,
with: owner,
isInline: false,
symbol: symbol,
sourceInfo: nil,
images: theImages))
}
}
}
Expand Down Expand Up @@ -505,21 +515,29 @@ public struct SymbolicatedBacktrace: CustomStringConvertible {
}

if let theSymbol = elf32Image?.lookupSymbol(address: relativeAddress) {
var location = try? elf32Image!.sourceLocation(for: relativeAddress)
var location: SourceLocation?

for inline in elf32Image!.inlineCallSites(at: relativeAddress) {
let fakeSymbol = Symbol(imageIndex: imageNdx,
imageName: theImages[imageNdx].name,
rawName: inline.rawName ?? "<unknown>",
offset: 0,
sourceLocation: location)
frames.append(Frame(captured: frame,
symbol: fakeSymbol,
inlined: true))

location = SourceLocation(path: inline.filename,
line: inline.line,
column: inline.column)
if showSourceLocations || showInlineFrames {
location = try? elf32Image!.sourceLocation(for: relativeAddress)
} else {
location = nil
}

if showInlineFrames {
for inline in elf32Image!.inlineCallSites(at: relativeAddress) {
let fakeSymbol = Symbol(imageIndex: imageNdx,
imageName: theImages[imageNdx].name,
rawName: inline.rawName ?? "<unknown>",
offset: 0,
sourceLocation: location)
frames.append(Frame(captured: frame,
symbol: fakeSymbol,
inlined: true))

location = SourceLocation(path: inline.filename,
line: inline.line,
column: inline.column)
}
}

symbol = Symbol(imageIndex: imageNdx,
Expand All @@ -528,21 +546,29 @@ public struct SymbolicatedBacktrace: CustomStringConvertible {
offset: theSymbol.offset,
sourceLocation: location)
} else if let theSymbol = elf64Image?.lookupSymbol(address: relativeAddress) {
var location = try? elf64Image!.sourceLocation(for: relativeAddress)
var location: SourceLocation?

for inline in elf64Image!.inlineCallSites(at: relativeAddress) {
let fakeSymbol = Symbol(imageIndex: imageNdx,
imageName: theImages[imageNdx].name,
rawName: inline.rawName ?? "<unknown>",
offset: 0,
sourceLocation: location)
frames.append(Frame(captured: frame,
symbol: fakeSymbol,
inlined: true))

location = SourceLocation(path: inline.filename,
line: inline.line,
column: inline.column)
if showSourceLocations || showInlineFrames {
location = try? elf64Image!.sourceLocation(for: relativeAddress)
} else {
location = nil
}

if showInlineFrames {
for inline in elf64Image!.inlineCallSites(at: relativeAddress) {
let fakeSymbol = Symbol(imageIndex: imageNdx,
imageName: theImages[imageNdx].name,
rawName: inline.rawName ?? "<unknown>",
offset: 0,
sourceLocation: location)
frames.append(Frame(captured: frame,
symbol: fakeSymbol,
inlined: true))

location = SourceLocation(path: inline.filename,
line: inline.line,
column: inline.column)
}
}

symbol = Symbol(imageIndex: imageNdx,
Expand Down
106 changes: 85 additions & 21 deletions stdlib/public/libexec/swift-backtrace/TargetLinux.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,18 @@ import _Backtracing

@_implementationOnly import Runtime

enum SomeBacktrace {
case raw(Backtrace)
case symbolicated(SymbolicatedBacktrace)
}

struct TargetThread {
typealias ThreadID = pid_t

var id: ThreadID
var context: HostContext?
var name: String
var backtrace: SymbolicatedBacktrace
var backtrace: SomeBacktrace
}

class Target {
Expand Down Expand Up @@ -105,7 +110,8 @@ class Target {
return trimmed
}

init(crashInfoAddr: UInt64, limit: Int?, top: Int, cache: Bool) {
init(crashInfoAddr: UInt64, limit: Int?, top: Int, cache: Bool,
symbolicate: SwiftBacktrace.Symbolication) {
// fd #4 is reserved for the memory server
let memserverFd: CInt = 4

Expand All @@ -130,7 +136,8 @@ class Target {

do {
try fetchThreads(threadListHead: Address(crashInfo.thread_list),
limit: limit, top: top, cache: cache)
limit: limit, top: top, cache: cache,
symbolicate: symbolicate)
} catch {
print("swift-backtrace: failed to fetch thread information: \(error)")
exit(1)
Expand All @@ -143,7 +150,8 @@ class Target {
/// uninterruptible wait, we won't have a ucontext for it.
func fetchThreads(
threadListHead: Address,
limit: Int?, top: Int, cache: Bool
limit: Int?, top: Int, cache: Bool,
symbolicate: SwiftBacktrace.Symbolication
) throws {
var next = threadListHead

Expand All @@ -164,18 +172,46 @@ class Target {
images: images,
limit: limit,
top: top)
guard let symbolicated
= backtrace.symbolicated(with: images,
sharedCacheInfo: nil,
useSymbolCache: cache) else {
print("unable to symbolicate backtrace for thread \(t.tid)")
exit(1)

let shouldSymbolicate: Bool
let showInlineFrames: Bool
let showSourceLocations: Bool
switch symbolicate {
case .off:
shouldSymbolicate = false
showInlineFrames = false
showSourceLocations = false
case .fast:
shouldSymbolicate = true
showInlineFrames = false
showSourceLocations = false
case .full:
shouldSymbolicate = true
showInlineFrames = true
showSourceLocations = true
}

threads.append(TargetThread(id: TargetThread.ThreadID(t.tid),
context: context,
name: getThreadName(tid: t.tid),
backtrace: symbolicated))
if shouldSymbolicate {
guard let symbolicated
= backtrace.symbolicated(with: images,
sharedCacheInfo: nil,
showInlineFrames: showInlineFrames,
showSourceLocations: showSourceLocations,
useSymbolCache: cache) else {
print("unable to symbolicate backtrace for thread \(t.tid)")
exit(1)
}

threads.append(TargetThread(id: TargetThread.ThreadID(t.tid),
context: context,
name: getThreadName(tid: t.tid),
backtrace: .symbolicated(symbolicated)))
} else {
threads.append(TargetThread(id: TargetThread.ThreadID(t.tid),
context: context,
name: getThreadName(tid: t.tid),
backtrace: .raw(backtrace)))
}
}

// Sort the threads by thread ID; the main thread always sorts
Expand All @@ -193,7 +229,10 @@ class Target {
}
}

func redoBacktraces(limit: Int?, top: Int, cache: Bool) {
func redoBacktraces(
limit: Int?, top: Int, cache: Bool,
symbolicate: SwiftBacktrace.Symbolication
) {
for (ndx, thread) in threads.enumerated() {
guard let context = thread.context else {
continue
Expand All @@ -208,14 +247,39 @@ class Target {
continue
}

guard let symbolicated = backtrace.symbolicated(with: images,
sharedCacheInfo: nil,
useSymbolCache: cache) else {
print("unable to symbolicate backtrace from context for thread \(ndx)")
continue
let shouldSymbolicate: Bool
let showInlineFrames: Bool
let showSourceLocations: Bool
switch symbolicate {
case .off:
shouldSymbolicate = false
showInlineFrames = false
showSourceLocations = false
case .fast:
shouldSymbolicate = true
showInlineFrames = false
showSourceLocations = false
case .full:
shouldSymbolicate = true
showInlineFrames = true
showSourceLocations = true
}

threads[ndx].backtrace = symbolicated
if shouldSymbolicate {
guard let symbolicated = backtrace.symbolicated(
with: images,
sharedCacheInfo: nil,
showInlineFrames: showInlineFrames,
showSourceLocations: showSourceLocations,
useSymbolCache: cache) else {
print("unable to symbolicate backtrace from context for thread \(ndx)")
continue
}

threads[ndx].backtrace = .symbolicated(symbolicated)
} else {
threads[ndx].backtrace = .raw(backtrace)
}
}
}

Expand Down
Loading