-
Notifications
You must be signed in to change notification settings - Fork 108
Optionally symbolicate backtraces. #676
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
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
02bedf7
Optionally symbolicate backtraces.
grynspan fd48220
Rather than have a separate .preciseDemangled, just output the backtr…
grynspan 8e235ef
Mark new stuff experimental
grynspan 2958be9
Simplify demangling, don't do C++ demangling for now
grynspan c2e2df4
Fix up header includes
grynspan File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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
42 changes: 42 additions & 0 deletions
42
Sources/Testing/ABI/v0/Encoded/ABIv0.EncodedBacktrace.swift
This file contains hidden or 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,42 @@ | ||
// | ||
// This source file is part of the Swift.org open source project | ||
// | ||
// Copyright (c) 2024 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 Swift project authors | ||
// | ||
|
||
extension ABIv0 { | ||
/// A type implementing the JSON encoding of ``Backtrace`` for the ABI entry | ||
/// point and event stream output. | ||
/// | ||
/// This type is not part of the public interface of the testing library. It | ||
/// assists in converting values to JSON; clients that consume this JSON are | ||
/// expected to write their own decoders. | ||
struct EncodedBacktrace: Sendable { | ||
/// The frames in the backtrace. | ||
var symbolicatedAddresses: [Backtrace.SymbolicatedAddress] | ||
grynspan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
init(encoding backtrace: borrowing Backtrace, in eventContext: borrowing Event.Context) { | ||
grynspan marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if let symbolicationMode = eventContext.configuration?.backtraceSymbolicationMode { | ||
symbolicatedAddresses = backtrace.symbolicate(symbolicationMode) | ||
} else { | ||
symbolicatedAddresses = backtrace.addresses.map { Backtrace.SymbolicatedAddress(address: $0) } | ||
} | ||
} | ||
} | ||
} | ||
|
||
// MARK: - Codable | ||
|
||
extension ABIv0.EncodedBacktrace: Codable { | ||
func encode(to encoder: any Encoder) throws { | ||
try symbolicatedAddresses.encode(to: encoder) | ||
} | ||
|
||
init(from decoder: any Decoder) throws { | ||
self.symbolicatedAddresses = try [Backtrace.SymbolicatedAddress](from: decoder) | ||
} | ||
} |
This file contains hidden or 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 hidden or 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 hidden or 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 hidden or 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
167 changes: 167 additions & 0 deletions
167
Sources/Testing/SourceAttribution/Backtrace+Symbolication.swift
This file contains hidden or 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,167 @@ | ||
// | ||
// This source file is part of the Swift.org open source project | ||
// | ||
// Copyright (c) 2024 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 Swift project authors | ||
// | ||
|
||
private import _TestingInternals | ||
|
||
/// A type representing a backtrace or stack trace. | ||
@_spi(Experimental) @_spi(ForToolsIntegrationOnly) | ||
extension Backtrace { | ||
/// An enumeration describing the symbolication mode to use when handling | ||
/// events containing backtraces. | ||
public enum SymbolicationMode: Sendable { | ||
/// The backtrace should be symbolicated, but no demangling should be | ||
/// performed. | ||
case mangled | ||
|
||
/// The backtrace should be symbolicated and Swift symbols should be | ||
/// demangled if possible. | ||
/// | ||
/// "Foreign" symbol names such as those produced by C++ are not demangled. | ||
case demangled | ||
} | ||
|
||
/// A type representing an instance of ``Backtrace/Address`` that has been | ||
/// symbolicated by a call to ``Backtrace/symbolicate(_:)``. | ||
public struct SymbolicatedAddress: Sendable { | ||
/// The (unsymbolicated) address from the backtrace. | ||
public var address: Address | ||
|
||
/// The offset of ``address`` from the start of the corresponding function, | ||
/// if available. | ||
/// | ||
/// If ``address`` could not be resolved to a symbol, the value of this | ||
/// property is `nil`. | ||
public var offset: UInt64? | ||
|
||
/// The name of the symbol at ``address``, if available. | ||
/// | ||
/// If ``address`` could not be resolved to a symbol, the value of this | ||
/// property is `nil`. | ||
public var symbolName: String? | ||
} | ||
|
||
/// Symbolicate the addresses in this backtrace. | ||
/// | ||
/// - Parameters: | ||
/// - mode: How to symbolicate the addresses in the backtrace. | ||
/// | ||
/// - Returns: An array of strings representing the names of symbols in | ||
/// `addresses`. | ||
/// | ||
/// If an address in `addresses` cannot be symbolicated, the corresponding | ||
/// instance of ``SymbolicatedAddress`` in the resulting array has a `nil` | ||
/// value for its ``Backtrace/SymbolicatedAddress/symbolName`` property. | ||
public func symbolicate(_ mode: SymbolicationMode) -> [SymbolicatedAddress] { | ||
var result = addresses.map { SymbolicatedAddress(address: $0) } | ||
|
||
#if SWT_TARGET_OS_APPLE | ||
for (i, address) in addresses.enumerated() { | ||
var info = Dl_info() | ||
if 0 != dladdr(UnsafePointer(bitPattern: UInt(clamping: address)), &info) { | ||
let offset = address - Address(clamping: UInt(bitPattern: info.dli_saddr)) | ||
let symbolName = info.dli_sname.flatMap(String.init(validatingCString:)) | ||
result[i] = SymbolicatedAddress(address: address, offset: offset, symbolName: symbolName) | ||
} | ||
} | ||
#elseif os(Linux) | ||
// Although Linux has dladdr(), it does not have symbol names from ELF | ||
// binaries by default. The standard library's backtracing functionality has | ||
// implemented sufficient ELF/DWARF parsing to be able to symbolicate Linux | ||
// backtraces. TODO: adopt the standard library's Backtrace on Linux | ||
// Note that this means on Linux we don't have demangling capability (since | ||
// we don't have the mangled symbol names in the first place) so this code | ||
// does not check the mode argument. | ||
#elseif os(Windows) | ||
_withDbgHelpLibrary { hProcess in | ||
guard let hProcess else { | ||
return | ||
} | ||
for (i, address) in addresses.enumerated() { | ||
withUnsafeTemporaryAllocation(of: SYMBOL_INFO_PACKAGEW.self, capacity: 1) { symbolInfo in | ||
let symbolInfo = symbolInfo.baseAddress! | ||
symbolInfo.pointee.si.SizeOfStruct = ULONG(MemoryLayout<SYMBOL_INFOW>.stride) | ||
symbolInfo.pointee.si.MaxNameLen = ULONG(MAX_SYM_NAME) | ||
var displacement = DWORD64(0) | ||
if SymFromAddrW(hProcess, DWORD64(clamping: address), &displacement, symbolInfo.pointer(to: \.si)!) { | ||
let symbolName = String.decodeCString(symbolInfo.pointer(to: \.si.Name)!, as: UTF16.self)?.result | ||
result[i] = SymbolicatedAddress(address: address, offset: displacement, symbolName: symbolName) | ||
} | ||
} | ||
} | ||
} | ||
#elseif os(WASI) | ||
// WASI does not currently support backtracing let alone symbolication. | ||
#else | ||
#warning("Platform-specific implementation missing: backtrace symbolication unavailable") | ||
#endif | ||
|
||
if mode != .mangled { | ||
result = result.map { symbolicatedAddress in | ||
var symbolicatedAddress = symbolicatedAddress | ||
if let demangledName = symbolicatedAddress.symbolName.flatMap(_demangle) { | ||
symbolicatedAddress.symbolName = demangledName | ||
} | ||
return symbolicatedAddress | ||
} | ||
} | ||
|
||
return result | ||
} | ||
} | ||
|
||
// MARK: - Codable | ||
|
||
extension Backtrace.SymbolicatedAddress: Codable {} | ||
|
||
// MARK: - Swift runtime wrappers | ||
|
||
/// Demangle a symbol name. | ||
/// | ||
/// - Parameters: | ||
/// - mangledSymbolName: The symbol name to demangle. | ||
/// | ||
/// - Returns: The demangled form of `mangledSymbolName` according to the | ||
/// Swift standard library or the platform's C++ standard library, or `nil` | ||
/// if the symbol name could not be demangled. | ||
private func _demangle(_ mangledSymbolName: String) -> String? { | ||
mangledSymbolName.withCString { mangledSymbolName in | ||
guard let demangledSymbolName = swift_demangle(mangledSymbolName, strlen(mangledSymbolName), nil, nil, 0) else { | ||
return nil | ||
} | ||
defer { | ||
free(demangledSymbolName) | ||
} | ||
return String(validatingCString: demangledSymbolName) | ||
} | ||
} | ||
|
||
#if os(Windows) | ||
/// Configure the environment to allow calling into the Debug Help library. | ||
/// | ||
/// - Parameters: | ||
/// - body: A function to invoke. A process handle valid for use with Debug | ||
/// Help functions is passed in, or `nullptr` if the Debug Help library | ||
/// could not be initialized. | ||
/// - context: An arbitrary pointer to pass to `body`. | ||
/// | ||
/// On Windows, the Debug Help library (DbgHelp.lib) is not thread-safe. All | ||
/// calls into it from the Swift runtime and stdlib should route through this | ||
/// function. | ||
private func _withDbgHelpLibrary(_ body: (HANDLE?) -> Void) { | ||
withoutActuallyEscaping(body) { body in | ||
withUnsafePointer(to: body) { context in | ||
_swift_win32_withDbgHelpLibrary({ hProcess, context in | ||
let body = context!.load(as: ((HANDLE?) -> Void).self) | ||
body(hProcess) | ||
}, .init(mutating: context)) | ||
} | ||
} | ||
} | ||
#endif |
This file contains hidden or 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,60 @@ | ||
// | ||
// This source file is part of the Swift.org open source project | ||
// | ||
// Copyright (c) 2024 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 Swift project authors | ||
// | ||
|
||
#if !defined(SWT_DEMANGLE_H) | ||
#define SWT_DEMANGLE_H | ||
|
||
#include "Defines.h" | ||
#include "Includes.h" | ||
|
||
SWT_ASSUME_NONNULL_BEGIN | ||
|
||
/// Demangle a Swift symbol name. | ||
/// | ||
/// - Parameters: | ||
/// - mangledName: A pointer to the mangled symbol name to demangle. | ||
/// - mangledNameLength: The length of `mangledName` in bytes, not including | ||
/// any trailing null byte. | ||
/// - outputBuffer: Unused by the testing library. Pass `nullptr`. | ||
/// - outputBufferSize: Unused by the testing library. Pass `nullptr`. | ||
/// - flags: Unused by the testing library. Pass `0`. | ||
/// | ||
/// - Returns: The demangled form of `mangledName`, or `nullptr` if demangling | ||
/// failed. The caller is responsible for freeing this string with `free()` | ||
/// when done. | ||
SWT_IMPORT_FROM_STDLIB char *_Nullable swift_demangle( | ||
const char *mangledName, | ||
size_t mangledNameLength, | ||
char *_Nullable outputBuffer, | ||
size_t *_Nullable outputBufferSize, | ||
uint32_t flags | ||
); | ||
|
||
#if defined(_WIN32) | ||
/// Configure the environment to allow calling into the Debug Help library. | ||
/// | ||
/// - Parameters: | ||
/// - body: A function to invoke. A process handle valid for use with Debug | ||
/// Help functions is passed in, or `nullptr` if the Debug Help library | ||
/// could not be initialized. | ||
/// - context: An arbitrary pointer to pass to `body`. | ||
/// | ||
/// On Windows, the Debug Help library (DbgHelp.lib) is not thread-safe. All | ||
/// calls into it from the Swift runtime and stdlib should route through this | ||
/// function. | ||
SWT_IMPORT_FROM_STDLIB void _swift_win32_withDbgHelpLibrary( | ||
void (* body)(HANDLE _Nullable hProcess, void *_Null_unspecified context), | ||
void *_Null_unspecified context | ||
); | ||
#endif | ||
|
||
SWT_ASSUME_NONNULL_END | ||
|
||
#endif |
This file contains hidden or 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
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.