From f9984601275e1e2770292f013514776c82e0a115 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Mon, 13 Feb 2023 14:26:01 +0000 Subject: [PATCH 01/22] [Runtime] [Darwin] Add code to trigger the external backtracer. When a Swift program crashes, we should catch the crash and execute the external backtracer. rdar://105391747 --- include/swift/Runtime/Backtrace.h | 117 +++ include/swift/Runtime/Config.h | 28 + stdlib/public/runtime/Backtrace.cpp | 755 ++++++++++++++++++ stdlib/public/runtime/CMakeLists.txt | 2 + stdlib/public/runtime/CrashHandlerMacOS.cpp | 448 +++++++++++ .../public/runtime/EnvironmentVariables.def | 5 + 6 files changed, 1355 insertions(+) create mode 100644 include/swift/Runtime/Backtrace.h create mode 100644 stdlib/public/runtime/Backtrace.cpp create mode 100644 stdlib/public/runtime/CrashHandlerMacOS.cpp diff --git a/include/swift/Runtime/Backtrace.h b/include/swift/Runtime/Backtrace.h new file mode 100644 index 0000000000000..db69df602c4c3 --- /dev/null +++ b/include/swift/Runtime/Backtrace.h @@ -0,0 +1,117 @@ +//===--- Backtrace.cpp - Swift crash catching and backtracing support ---- ===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 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 +// +//===----------------------------------------------------------------------===// +// +// Definitions relating to backtracing. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_RUNTIME_BACKTRACE_H +#define SWIFT_RUNTIME_BACKTRACE_H + +#include "swift/Runtime/Config.h" + +#include "swift/shims/Visibility.h" +#include "swift/shims/_SwiftBacktracing.h" + +#include + +#ifdef __cplusplus +namespace swift { +namespace runtime { +namespace backtrace { +#endif + +#ifdef _WIN32 +typedef wchar_t ArgChar; +typedef DWORD ErrorCode; +#else +typedef char ArgChar; +typedef int ErrorCode; +#endif + +SWIFT_RUNTIME_STDLIB_INTERNAL ErrorCode _swift_installCrashHandler(); + +SWIFT_RUNTIME_STDLIB_INTERNAL bool _swift_spawnBacktracer(const ArgChar * const *argv); + +enum class UnwindAlgorithm { + Auto = 0, + Fast = 1, + Precise = 2 +}; + +enum class OnOffTty { + Off = 0, + On = 1, + TTY = 2 +}; + +enum class Preset { + Auto = -1, + Friendly = 0, + Medium = 1, + Full = 2 +}; + +enum class ThreadsToShow { + Preset = -1, + All = 0, + Crashed = 1 +}; + +enum class RegistersToShow { + Preset = -1, + None = 0, + All = 1, + Crashed = 2 +}; + +enum class ImagesToShow { + Preset = -1, + None = 0, + All = 1, + Mentioned = 2 +}; + +enum class SanitizePaths { + Preset = -1, + Off = 0, + On = 1 +}; + +struct BacktraceSettings { + UnwindAlgorithm algorithm; + OnOffTty enabled; + bool demangle; + OnOffTty interactive; + OnOffTty color; + unsigned timeout; + ThreadsToShow threads; + RegistersToShow registers; + ImagesToShow images; + unsigned limit; + unsigned top; + SanitizePaths sanitize; + Preset preset; + const char *swiftBacktracePath; +}; + +SWIFT_RUNTIME_STDLIB_INTERNAL BacktraceSettings _swift_backtraceSettings; + +SWIFT_RUNTIME_STDLIB_SPI SWIFT_CC(swift) bool _swift_isThunkFunction(const char *mangledName); + +#ifdef __cplusplus +} // namespace backtrace +} // namespace runtime +} // namespace swift +#endif + +#endif // SWIFT_RUNTIME_BACKTRACE_H diff --git a/include/swift/Runtime/Config.h b/include/swift/Runtime/Config.h index 28d6dd86b8fa5..ac44146508516 100644 --- a/include/swift/Runtime/Config.h +++ b/include/swift/Runtime/Config.h @@ -481,6 +481,34 @@ swift_auth_code(T value, unsigned extra) { #endif } +/// Does this platform support backtrace-on-crash? +#ifdef __APPLE__ +# include +# if TARGET_OS_OSX +# define SWIFT_BACKTRACE_ON_CRASH_SUPPORTED 1 +# define SWIFT_BACKTRACE_SECTION "__DATA,swift5_backtrace" +# else +# define SWIFT_BACKTRACE_ON_CRASH_SUPPORTED 0 +# endif +#elif defined(_WIN32) +# define SWIFT_BACKTRACE_ON_CRASH_SUPPORTED 0 +# define SWIFT_BACKTRACE_SECTION ".sw5bckt" +#elif defined(__linux__) +# define SWIFT_BACKTRACE_ON_CRASH_SUPPORTED 0 +# define SWIFT_BACKTRACE_SECTION "swift5_backtrace" +#else +# define SWIFT_BACKTRACE_ON_CRASH_SUPPORTED 0 +#endif + +/// What is the system page size? +#if defined(__APPLE__) && defined(__arm64__) + // Apple Silicon systems use a 16KB page size + #define SWIFT_PAGE_SIZE 16384 +#else + // Everything else uses 4KB pages + #define SWIFT_PAGE_SIZE 4096 +#endif + #endif #endif // SWIFT_RUNTIME_CONFIG_H diff --git a/stdlib/public/runtime/Backtrace.cpp b/stdlib/public/runtime/Backtrace.cpp new file mode 100644 index 0000000000000..a1bcad306e1ea --- /dev/null +++ b/stdlib/public/runtime/Backtrace.cpp @@ -0,0 +1,755 @@ +//===--- Backtrace.cpp - Swift crash catching and backtracing support ---- ===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 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 +// +//===----------------------------------------------------------------------===// +// +// Crash catching and backtracing support routines. +// +//===----------------------------------------------------------------------===// + +#include + +#include "llvm/ADT/StringRef.h" + +#include "swift/Runtime/Config.h" +#include "swift/Runtime/Backtrace.h" +#include "swift/Runtime/Debug.h" +#include "swift/Runtime/Paths.h" +#include "swift/Runtime/EnvironmentVariables.h" +#include "swift/Runtime/Win32.h" + +#include "swift/Demangling/Demangler.h" + +#ifdef _WIN32 +#include +#else +#include +#include +#include +#endif + +#include +#include +#include + +#define DEBUG_BACKTRACING_SETTINGS 0 + +#ifndef lengthof +#define lengthof(x) (sizeof(x) / sizeof(x[0])) +#endif + +using namespace swift::runtime::backtrace; + +namespace swift { +namespace runtime { +namespace backtrace { + +SWIFT_RUNTIME_STDLIB_INTERNAL BacktraceSettings _swift_backtraceSettings = { + UnwindAlgorithm::Auto, + + // enabled +#if TARGET_OS_OSX + OnOffTty::TTY, +#elif 0 // defined(__linux__) || defined(_WIN32) + OnOffTty::On, +#else + OnOffTty::Off, +#endif + + // demangle + true, + + // interactive +#if TARGET_OS_OSX // || defined(__linux__) || defined(_WIN32) + OnOffTty::TTY, +#else + OnOffTty::Off, +#endif + + // color + OnOffTty::TTY, + + // timeout + 30, + + // threads + ThreadsToShow::Preset, + + // registers + RegistersToShow::Preset, + + // images + ImagesToShow::Preset, + + // limit + 64, + + // top + 16, + + // sanitize, + SanitizePaths::Preset, + + // preset + Preset::Auto, + + // swiftBacktracePath + NULL, +}; + +} +} +} + +namespace { + +class BacktraceInitializer { +public: + BacktraceInitializer(); +}; + +SWIFT_ALLOWED_RUNTIME_GLOBAL_CTOR_BEGIN + +BacktraceInitializer backtraceInitializer; + +SWIFT_ALLOWED_RUNTIME_GLOBAL_CTOR_END + +#if TARGET_OS_OSX +posix_spawnattr_t backtraceSpawnAttrs; +posix_spawn_file_actions_t backtraceFileActions; +#endif + +#if SWIFT_BACKTRACE_ON_CRASH_SUPPORTED + +// We need swiftBacktracePath to be aligned on a page boundary, and it also +// needs to be a multiple of the system page size. +#define SWIFT_BACKTRACE_BUFFER_SIZE 16384 + +static_assert((SWIFT_BACKTRACE_BUFFER_SIZE % SWIFT_PAGE_SIZE) == 0, + "The backtrace path buffer must be a multiple of the system " + "page size. If it isn't, you'll get weird crashes in other " + "code because we'll protect more than just the buffer."); + +// The same goes for swiftBacktraceEnvironment +#define SWIFT_BACKTRACE_ENVIRONMENT_SIZE 32768 + +static_assert((SWIFT_BACKTRACE_ENVIRONMENT_SIZE % SWIFT_PAGE_SIZE) == 0, + "The environment buffer must be a multiple of the system " + "page size. If it isn't, you'll get weird crashes in other " + "code because we'll protect more than just the buffer."); + +#if _WIN32 +#pragma section(SWIFT_BACKTRACE_SECTION, read, write) +__declspec(allocate(SWIFT_BACKTRACE_SECTION)) WCHAR swiftBacktracePath[SWIFT_BACKTRACE_BUFFER_SIZE]; +__declspec(allocate(SWIFT_BACKTRACE_SECTION)) CHAR swiftBacktraceEnv[SWIFT_BACKTRACE_ENVIRONMENT_SIZE]; +#elif defined(__linux__) || TARGET_OS_OSX +char swiftBacktracePath[SWIFT_BACKTRACE_BUFFER_SIZE] __attribute__((section(SWIFT_BACKTRACE_SECTION), aligned(SWIFT_PAGE_SIZE))); +char swiftBacktraceEnv[SWIFT_BACKTRACE_ENVIRONMENT_SIZE] __attribute__((section(SWIFT_BACKTRACE_SECTION), aligned(SWIFT_PAGE_SIZE))); +#endif + +void _swift_backtraceSetupEnvironment(); + +bool isStdoutATty() +{ +#ifndef _WIN32 + return isatty(STDOUT_FILENO); +#else + DWORD dwMode; + return GetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), &dwMode); +#endif +} + +bool isStdinATty() +{ +#ifndef _WIN32 + return isatty(STDIN_FILENO); +#else + DWORD dwMode; + return GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &dwMode); +#endif +} + +#endif // SWIFT_BACKTRACE_ON_CRASH_SUPPORTED + +void _swift_processBacktracingSetting(llvm::StringRef key, llvm::StringRef value); +void _swift_parseBacktracingSettings(const char *); + +#if DEBUG_BACKTRACING_SETTINGS +const char *algorithmToString(UnwindAlgorithm algorithm) { + switch (algorithm) { + case UnwindAlgorithm::Auto: return "Auto"; + case UnwindAlgorithm::Fast: return "Fast"; + case UnwindAlgorithm::Precise: return "Precise"; + } +} + +const char *onOffTtyToString(OnOffTty oot) { + switch (oot) { + case OnOffTty::On: return "On"; + case OnOffTty::Off: return "Off"; + case OnOffTty::TTY: return "TTY"; + } +} + +const char *boolToString(bool b) { + return b ? "true" : "false"; +} + +const char *presetToString(Preset preset) { + switch (preset) { + case Preset::Auto: return "Auto"; + case Preset::Friendly: return "Friendly"; + case Preset::Medium: return "Medium"; + case Preset::Full: return Full; + } +} +#endif + +} // namespace + +BacktraceInitializer::BacktraceInitializer() { + const char *backtracing = swift::runtime::environment::SWIFT_BACKTRACE(); + + if (backtracing) + _swift_parseBacktracingSettings(backtracing); + +#if TARGET_OS_OSX + // Make sure that we don't pass on setuid privileges, and that all fds + // are closed except for stdin/stdout/stderr. + posix_spawnattr_init(&backtraceSpawnAttrs); + posix_spawnattr_setflags(&backtraceSpawnAttrs, + POSIX_SPAWN_RESETIDS | POSIX_SPAWN_CLOEXEC_DEFAULT); + + posix_spawn_file_actions_init(&backtraceFileActions); + posix_spawn_file_actions_addinherit_np(&backtraceFileActions, STDIN_FILENO); + posix_spawn_file_actions_addinherit_np(&backtraceFileActions, STDOUT_FILENO); + posix_spawn_file_actions_addinherit_np(&backtraceFileActions, STDERR_FILENO); +#endif + +#if !SWIFT_BACKTRACE_ON_CRASH_SUPPORTED + if (_swift_backtraceSettings.enabled != OnOffTty::Off) { + swift::warning(0, + "swift runtime: backtrace-on-crash is not supported on " + "this platform.\n"); + _swift_backtraceSettings.enabled = OnOffTty::Off; + } +#else + if (_swift_backtraceSettings.enabled == OnOffTty::TTY) + _swift_backtraceSettings.enabled = + isStdoutATty() ? OnOffTty::On : OnOffTty::Off; + + if (_swift_backtraceSettings.interactive == OnOffTty::TTY) { + _swift_backtraceSettings.interactive = + (isStdoutATty() && isStdinATty()) ? OnOffTty::On : OnOffTty::Off; + } + + if (_swift_backtraceSettings.color == OnOffTty::TTY) + _swift_backtraceSettings.color = + isStdoutATty() ? OnOffTty::On : OnOffTty::Off; + + if (_swift_backtraceSettings.preset == Preset::Auto) { + if (_swift_backtraceSettings.interactive == OnOffTty::On) + _swift_backtraceSettings.preset = Preset::Friendly; + else + _swift_backtraceSettings.preset = Preset::Full; + } + + if (_swift_backtraceSettings.enabled == OnOffTty::On + && !_swift_backtraceSettings.swiftBacktracePath) { + _swift_backtraceSettings.swiftBacktracePath + = swift_getAuxiliaryExecutablePath("swift-backtrace"); + + if (!_swift_backtraceSettings.swiftBacktracePath) { + swift::warning(0, + "swift runtime: unable to locate swift-backtrace; " + "disabling backtracing.\n"); + _swift_backtraceSettings.enabled = OnOffTty::Off; + } + } + + if (_swift_backtraceSettings.enabled == OnOffTty::On) { + // Copy the path to swift-backtrace into swiftBacktracePath, then write + // protect it so that it can't be overwritten easily at runtime. We do + // this to avoid creating a massive security hole that would allow an + // attacker to overwrite the path and then cause a crash to get us to + // execute an arbitrary file. + +#if _WIN32 + if (_swift_backtraceSettings.algorithm == UnwindAlgorithm::Auto) + _swift_backtraceSettings.algorithm = UnwindAlgorithm::Precise; + + int len = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, + _swift_backtraceSettings.swiftBacktracePath, -1, + swiftBacktracePath, + SWIFT_BACKTRACE_BUFFER_SIZE); + if (!len) { + swift::warning(0, + "swift runtime: unable to convert path to " + "swift-backtrace: %08lx; disabling backtracing.\n", + ::GetLastError()); + _swift_backtraceSettings.enabled = OnOffTty::Off; + } else if (!VirtualProtect(swiftBacktracePath, + sizeof(swiftBacktracePath), + PAGE_READONLY, + NULL)) { + swift::warning(0, + "swift runtime: unable to protect path to " + "swift-backtrace: %08lx; disabling backtracing.\n", + ::GetLastError()); + _swift_backtraceSettings.enabled = OnOffTty::Off; + } + + _swift_backtraceSetupEnvironment(); + + if (!VirtualProtect(swiftBacktraceEnv, + sizeof(swiftBacktraceEnv), + PAGE_READONLY, + NULL)) { + swift::warning(0, + "swift runtime: unable to protect environment " + "for swift-backtrace: %08lx; disabling backtracing.\n", + ::GetLastError()); + _swift_backtraceSettings.enabled = OnOffTty::Off; + } +#else + if (_swift_backtraceSettings.algorithm == UnwindAlgorithm::Auto) + _swift_backtraceSettings.algorithm = UnwindAlgorithm::Precise; + + size_t len = strlen(_swift_backtraceSettings.swiftBacktracePath); + if (len > SWIFT_BACKTRACE_BUFFER_SIZE - 1) { + swift::warning(0, + "swift runtime: path to swift-backtrace is too long; " + "disabling backtracing.\n"); + _swift_backtraceSettings.enabled = OnOffTty::Off; + } else { + memcpy(swiftBacktracePath, + _swift_backtraceSettings.swiftBacktracePath, + len + 1); + + if (mprotect(swiftBacktracePath, + sizeof(swiftBacktracePath), + PROT_READ) < 0) { + swift::warning(0, + "swift runtime: unable to protect path to " + "swift-backtrace at %p: %d; disabling backtracing.\n", + swiftBacktracePath, + errno); + _swift_backtraceSettings.enabled = OnOffTty::Off; + } + } + + _swift_backtraceSetupEnvironment(); + + if (mprotect(swiftBacktraceEnv, + sizeof(swiftBacktraceEnv), + PROT_READ) < 0) { + swift::warning(0, + "swift runtime: unable to protect environment for " + "swift-backtrace at %p: %d; disabling backtracing.\n", + swiftBacktraceEnv, + errno); + _swift_backtraceSettings.enabled = OnOffTty::Off; + } +#endif + } + + if (_swift_backtraceSettings.enabled == OnOffTty::On) { + ErrorCode err = _swift_installCrashHandler(); + if (err != 0) { + swift::warning(0, + "swift runtime: crash handler installation failed; " + "disabling backtracing.\n"); + } + } +#endif + +#if DEBUG_BACKTRACING_SETTINGS + printf("\nBACKTRACING SETTINGS\n" + "\n" + "algorithm: %s\n" + "enabled: %s\n" + "demangle: %s\n" + "interactive: %s\n" + "color: %s\n" + "timeout: %u\n" + "preset: %s\n" + "swiftBacktracePath: %s\n", + algorithmToString(_swift_backtraceSettings.algorithm), + onOffTtyToString(_swift_backtraceSettings.enabled), + boolToString(_swift_backtraceSettings.demangle), + onOffTtyToString(_swift_backtraceSettings.interactive), + onOffTtyToString(_swift_backtraceSettings.color), + _swift_backtraceSettings.timeout, + presetToString(_swift_backtraceSettings.preset), + swiftBacktracePath); + + printf("\nBACKTRACING ENV\n"); + + const char *ptr = swiftBacktraceEnv; + while (*ptr) { + size_t len = std::strlen(ptr); + printf("%s\n", ptr); + ptr += len + 1; + } + printf("\n"); +#endif +} + +namespace { + +OnOffTty +parseOnOffTty(llvm::StringRef value) +{ + if (value.equals_insensitive("on") + || value.equals_insensitive("true") + || value.equals_insensitive("yes") + || value.equals_insensitive("y") + || value.equals_insensitive("t") + || value.equals_insensitive("1")) + return OnOffTty::On; + if (value.equals_insensitive("tty") + || value.equals_insensitive("auto")) + return OnOffTty::TTY; + return OnOffTty::Off; +} + +bool +parseBoolean(llvm::StringRef value) +{ + return (value.equals_insensitive("on") + || value.equals_insensitive("true") + || value.equals_insensitive("yes") + || value.equals_insensitive("y") + || value.equals_insensitive("t") + || value.equals_insensitive("1")); +} + +void +_swift_processBacktracingSetting(llvm::StringRef key, + llvm::StringRef value) + +{ + if (key.equals_insensitive("enable")) { + _swift_backtraceSettings.enabled = parseOnOffTty(value); + } else if (key.equals_insensitive("demangle")) { + _swift_backtraceSettings.demangle = parseBoolean(value); + } else if (key.equals_insensitive("interactive")) { + _swift_backtraceSettings.interactive = parseOnOffTty(value); + } else if (key.equals_insensitive("color")) { + _swift_backtraceSettings.color = parseOnOffTty(value); + } else if (key.equals_insensitive("timeout")) { + int count; + llvm::StringRef valueCopy = value; + + if (value.equals_insensitive("none")) { + _swift_backtraceSettings.timeout = 0; + } else if (!valueCopy.consumeInteger(0, count)) { + // Yes, consumeInteger() really does return *false* for success + llvm::StringRef unit = valueCopy.trim(); + + if (unit.empty() + || unit.equals_insensitive("s") + || unit.equals_insensitive("seconds")) + _swift_backtraceSettings.timeout = count; + else if (unit.equals_insensitive("m") + || unit.equals_insensitive("minutes")) + _swift_backtraceSettings.timeout = count * 60; + else if (unit.equals_insensitive("h") + || unit.equals_insensitive("hours")) + _swift_backtraceSettings.timeout = count * 3600; + + if (_swift_backtraceSettings.timeout < 0) { + swift::warning(0, + "swift runtime: bad backtracing timeout %ds\n", + _swift_backtraceSettings.timeout); + _swift_backtraceSettings.timeout = 0; + } + } else { + swift::warning(0, + "swift runtime: bad backtracing timeout '%.*s'\n", + static_cast(value.size()), value.data()); + } + } else if (key.equals_insensitive("unwind")) { + if (value.equals_insensitive("auto")) + _swift_backtraceSettings.algorithm = UnwindAlgorithm::Auto; + else if (value.equals_insensitive("fast")) + _swift_backtraceSettings.algorithm = UnwindAlgorithm::Fast; + else if (value.equals_insensitive("precise")) + _swift_backtraceSettings.algorithm = UnwindAlgorithm::Precise; + else { + swift::warning(0, + "swift runtime: unknown unwind algorithm '%.*s'\n", + static_cast(value.size()), value.data()); + } + } else if (key.equals_insensitive("sanitize")) { + _swift_backtraceSettings.sanitize + = parseBoolean(value) ? SanitizePaths::On : SanitizePaths::Off; + } else if (key.equals_insensitive("preset")) { + if (value.equals_insensitive("auto")) + _swift_backtraceSettings.preset = Preset::Auto; + else if (value.equals_insensitive("friendly")) + _swift_backtraceSettings.preset = Preset::Friendly; + else if (value.equals_insensitive("medium")) + _swift_backtraceSettings.preset = Preset::Medium; + else if (value.equals_insensitive("full")) + _swift_backtraceSettings.preset = Preset::Full; + else { + swift::warning(0, + "swift runtime: unknown backtracing preset '%.*s'\n", + static_cast(value.size()), value.data()); + } + } else if (key.equals_insensitive("threads")) { + if (value.equals_insensitive("all")) + _swift_backtraceSettings.threads = ThreadsToShow::All; + else if (value.equals_insensitive("crashed")) + _swift_backtraceSettings.threads = ThreadsToShow::Crashed; + else { + swift::warning(0, + "swift runtime: unknown threads setting '%.*s'\n", + static_cast(value.size()), value.data()); + } + } else if (key.equals_insensitive("registers")) { + if (value.equals_insensitive("none")) + _swift_backtraceSettings.registers = RegistersToShow::None; + else if (value.equals_insensitive("all")) + _swift_backtraceSettings.registers = RegistersToShow::All; + else if (value.equals_insensitive("crashed")) + _swift_backtraceSettings.registers = RegistersToShow::Crashed; + else { + swift::warning(0, + "swift runtime: unknown registers setting '%.*s'\n", + static_cast(value.size()), value.data()); + } + } else if (key.equals_insensitive("images")) { + if (value.equals_insensitive("none")) + _swift_backtraceSettings.images = ImagesToShow::None; + else if (value.equals_insensitive("all")) + _swift_backtraceSettings.images = ImagesToShow::All; + else if (value.equals_insensitive("mentioned")) + _swift_backtraceSettings.images = ImagesToShow::Mentioned; + else { + swift::warning(0, + "swift runtime: unknown registers setting '%.*s'\n", + static_cast(value.size()), value.data()); + } + } else if (key.equals_insensitive("limit")) { + int limit; + // Yes, getAsInteger() returns false for success. + if (value.equals_insensitive("none")) + _swift_backtraceSettings.limit = -1; + else if (!value.getAsInteger(0, limit) && limit > 0) + _swift_backtraceSettings.limit = limit; + else { + swift::warning(0, + "swift runtime: bad backtrace limit '%.*s'\n", + static_cast(value.size()), value.data()); + } + } else if (key.equals_insensitive("top")) { + int top; + // (If you think the next line is wrong, see above.) + if (!value.getAsInteger(0, top) && top >= 0) + _swift_backtraceSettings.top = top; + else { + swift::warning(0, + "swift runtime: bad backtrace top count '%.*s'\n", + static_cast(value.size()), value.data()); + } + } else if (key.equals_insensitive("swift-backtrace")) { + size_t len = value.size(); + char *path = (char *)std::malloc(len + 1); + std::copy(value.begin(), value.end(), path); + path[len] = 0; + + std::free(const_cast(_swift_backtraceSettings.swiftBacktracePath)); + _swift_backtraceSettings.swiftBacktracePath = path; + } else { + swift::warning(0, + "swift runtime: unknown backtracing setting '%.*s'\n", + static_cast(key.size()), key.data()); + } +} + +void +_swift_parseBacktracingSettings(const char *settings) +{ + const char *ptr = settings; + const char *key = ptr; + const char *keyEnd; + const char *value; + const char *valueEnd; + enum { + ScanningKey, + ScanningValue + } state = ScanningKey; + int ch; + + while ((ch = *ptr++)) { + switch (state) { + case ScanningKey: + if (ch == '=') { + keyEnd = ptr - 1; + value = ptr; + state = ScanningValue; + continue; + } + break; + case ScanningValue: + if (ch == ',') { + valueEnd = ptr - 1; + + _swift_processBacktracingSetting(llvm::StringRef(key, keyEnd - key), + llvm::StringRef(value, + valueEnd - value)); + + key = ptr; + state = ScanningKey; + continue; + } + break; + } + } + + if (state == ScanningValue) { + valueEnd = ptr - 1; + _swift_processBacktracingSetting(llvm::StringRef(key, keyEnd - key), + llvm::StringRef(value, + valueEnd - value)); + } +} + +#if SWIFT_BACKTRACE_ON_CRASH_SUPPORTED +// These are the only environment variables that are passed through to +// the swift-backtrace process. They're copied at program start, and then +// write protected so they can't be manipulated by an attacker using a buffer +// overrun. +const char * const environmentVarsToPassThrough[] = { + "LD_LIBRARY_PATH", + "DYLD_LIBRARY_PATH", + "DYLD_FRAMEWORK_PATH", + "PATH", + "TERM", + "LANG", + "HOME" +}; + +#define BACKTRACE_MAX_ENV_VARS lengthof(environmentVarsToPassThrough) + +void +_swift_backtraceSetupEnvironment() +{ + size_t remaining = sizeof(swiftBacktraceEnv); + char *penv = swiftBacktraceEnv; + + std::memset(swiftBacktraceEnv, 0, sizeof(swiftBacktraceEnv)); + + // We definitely don't want this on in the swift-backtrace program + const char * const disable = "SWIFT_BACKTRACE=enable=no"; + const size_t disableLen = std::strlen(disable) + 1; + std::memcpy(penv, disable, disableLen); + penv += disableLen; + remaining -= disableLen; + + for (unsigned n = 0; n < BACKTRACE_MAX_ENV_VARS; ++n) { + const char *name = environmentVarsToPassThrough[n]; + const char *value = getenv(name); + if (!value) + continue; + + size_t nameLen = std::strlen(name); + size_t valueLen = std::strlen(value); + size_t totalLen = nameLen + 1 + valueLen + 1; + + if (remaining > totalLen) { + std::memcpy(penv, name, nameLen); + penv += nameLen; + *penv++ = '='; + std::memcpy(penv, value, valueLen); + penv += valueLen; + *penv++ = 0; + + remaining -= totalLen; + } + } + + *penv = 0; +} + +#endif // SWIFT_BACKTRACE_ON_CRASH_SUPPORTED + +} // namespace + +namespace swift { +namespace runtime { +namespace backtrace { + +/// Test if a Swift symbol name represents a thunk function. +/// +/// In backtraces, it is often desirable to omit thunk frames as they usually +/// just clutter up the backtrace unnecessarily. +/// +/// @param mangledName is the symbol name to be tested. +/// +/// @returns `true` if `mangledName` represents a thunk function. +SWIFT_RUNTIME_STDLIB_SPI SWIFT_CC(swift) bool +_swift_isThunkFunction(const char *mangledName) { + swift::Demangle::Context ctx; + + return ctx.isThunkSymbol(mangledName); +} + +// N.B. THIS FUNCTION MUST BE SAFE TO USE FROM A CRASH HANDLER. On Linux +// and macOS, that means it must be async-signal-safe. On Windows, there +// isn't an equivalent notion but a similar restriction applies. +SWIFT_RUNTIME_STDLIB_INTERNAL bool +_swift_spawnBacktracer(const ArgChar * const *argv) +{ +#if TARGET_OS_OSX + pid_t child; + const char *env[BACKTRACE_MAX_ENV_VARS + 1]; + + // Set-up the environment array + const char *ptr = swiftBacktraceEnv; + unsigned nEnv = 0; + while (*ptr && nEnv < lengthof(env) - 1) { + env[nEnv++] = ptr; + ptr += std::strlen(ptr) + 1; + }; + env[nEnv] = 0; + + // SUSv3 says argv and envp are "completely constant" and that the reason + // posix_spawn() et al use char * const * is for compatibility. + int ret = posix_spawn(&child, swiftBacktracePath, + &backtraceFileActions, &backtraceSpawnAttrs, + const_cast(argv), + const_cast(env)); + if (ret < 0) + return false; + + int wstatus; + + do { + ret = waitpid(child, &wstatus, 0); + } while (ret < 0 && errno == EINTR); + + if (WIFEXITED(wstatus)) + return WEXITSTATUS(wstatus) == 0; + + return false; + + // ###TODO: Linux + // ###TODO: Windows +#else + return false; +#endif +} + +} // namespace backtrace +} // namespace runtime +} // namespace swift diff --git a/stdlib/public/runtime/CMakeLists.txt b/stdlib/public/runtime/CMakeLists.txt index 87ad25f452ff9..3331b6871eee3 100644 --- a/stdlib/public/runtime/CMakeLists.txt +++ b/stdlib/public/runtime/CMakeLists.txt @@ -30,9 +30,11 @@ set(swift_runtime_sources AnyHashableSupport.cpp Array.cpp AutoDiffSupport.cpp + Backtrace.cpp Bincompat.cpp BytecodeLayouts.cpp Casting.cpp + CrashHandlerMacOS.cpp CrashReporter.cpp Demangle.cpp DynamicCast.cpp diff --git a/stdlib/public/runtime/CrashHandlerMacOS.cpp b/stdlib/public/runtime/CrashHandlerMacOS.cpp new file mode 100644 index 0000000000000..ce2fe711a2cfa --- /dev/null +++ b/stdlib/public/runtime/CrashHandlerMacOS.cpp @@ -0,0 +1,448 @@ +//===--- CrashHandlerMacOS.cpp - Swift crash handler for macOS ----------- ===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 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 +// +//===----------------------------------------------------------------------===// +// +// The macOS crash handler implementation. +// +// We use signal handling rather than trying to use Mach exceptions here, +// because the latter would entail running a separate Mach server thread, and +// creates a much greater risk of interfering with the system wide Crash +// Reporter, which is a no-no. +// +//===----------------------------------------------------------------------===// + +#ifdef __APPLE__ + +#include + +#if TARGET_OS_OSX + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "swift/Runtime/Backtrace.h" + +#include + +#ifndef lengthof +#define lengthof(x) (sizeof(x) / sizeof(x[0])) +#endif + +using namespace swift::runtime::backtrace; + +namespace { + +void handle_fatal_signal(int signum, siginfo_t *pinfo, void *uctx); +void suspend_other_threads(); +void resume_other_threads(); +bool run_backtracer(void); + +swift::CrashInfo crashInfo; + +os_unfair_lock crashLock = OS_UNFAIR_LOCK_INIT; + +const int signalsToHandle[] = { + SIGQUIT, + SIGABRT, + SIGBUS, + SIGFPE, + SIGILL, + SIGSEGV, + SIGTRAP +}; + +} // namespace + +namespace swift { +namespace runtime { +namespace backtrace { + +SWIFT_RUNTIME_STDLIB_INTERNAL int +_swift_installCrashHandler() +{ + stack_t ss; + + // Install an alternate signal handling stack + ss.ss_flags = 0; + ss.ss_size = SIGSTKSZ; + ss.ss_sp = mmap(0, ss.ss_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (ss.ss_sp == MAP_FAILED) + return errno; + + if (sigaltstack(&ss, 0) < 0) + return errno; + + // Now register signal handlers + struct sigaction sa; + + sigfillset(&sa.sa_mask); + for (unsigned n = 0; n < lengthof(signalsToHandle); ++n) { + sigdelset(&sa.sa_mask, signalsToHandle[n]); + } + + sa.sa_handler = NULL; + + for (unsigned n = 0; n < lengthof(signalsToHandle); ++n) { + sa.sa_flags = SA_ONSTACK | SA_SIGINFO | SA_NODEFER; + sa.sa_sigaction = handle_fatal_signal; + + if (sigaction(signalsToHandle[n], &sa, NULL) < 0) + return errno; + } + + return 0; +} + +} // namespace backtrace +} // namespace runtime +} // namespace swift + +namespace { + +void +suspend_other_threads() +{ + os_unfair_lock_lock(&crashLock); + + thread_t self = mach_thread_self(); + thread_act_array_t threads; + mach_msg_type_number_t count = 0; + + kern_return_t kr = task_threads(mach_task_self(), &threads, &count); + + if (kr != KERN_SUCCESS) + return; + + for (unsigned n = 0; n < count; ++n) { + if (threads[n] == self) + continue; + + // Ignore the results of these two; if they fail there's nothing we can do + (void)thread_resume(threads[n]); + (void)mach_port_deallocate(mach_task_self(), threads[n]); + } + + vm_deallocate(mach_task_self(), + (vm_address_t)threads, + count * sizeof(threads[0])); + + os_unfair_lock_unlock(&crashLock); +} + +void +resume_other_threads() +{ + os_unfair_lock_lock(&crashLock); + + thread_t self = mach_thread_self(); + thread_act_array_t threads; + mach_msg_type_number_t count = 0; + + kern_return_t kr = task_threads(mach_task_self(), &threads, &count); + + if (kr != KERN_SUCCESS) + return; + + for (unsigned n = 0; n < count; ++n) { + if (threads[n] == self) + continue; + + // Ignore the results of these two; if they fail there's nothing we can do + (void)thread_resume(threads[n]); + (void)mach_port_deallocate(mach_task_self(), threads[n]); + } + + vm_deallocate(mach_task_self(), + (vm_address_t)threads, + count * sizeof(threads[0])); + + os_unfair_lock_unlock(&crashLock); +} + +void +handle_fatal_signal(int signum, + siginfo_t *pinfo, + void *uctx) +{ + int old_err = errno; + + // Prevent this from exploding if more than one thread gets here at once + suspend_other_threads(); + + // Remove our signal handlers; crashes should kill us here + for (unsigned n = 0; n < lengthof(signalsToHandle); ++n) + signal(signalsToHandle[n], SIG_DFL); + + // Get our thread identifier + thread_identifier_info_data_t ident_info; + mach_msg_type_number_t ident_size = THREAD_IDENTIFIER_INFO_COUNT; + + int ret = thread_info(mach_thread_self(), + THREAD_IDENTIFIER_INFO, + (int *)&ident_info, + &ident_size); + if (ret != KERN_SUCCESS) + return; + + // Fill in crash info + crashInfo.crashing_thread = ident_info.thread_id; + crashInfo.signal = signum; + crashInfo.fault_address = (uint64_t)pinfo->si_addr; + crashInfo.mctx = (uint64_t)(((ucontext_t *)uctx)->uc_mcontext); + + /* Start the backtracer; this will suspend the process, so there's no need + to try to suspend other threads from here. */ + run_backtracer(); + + // Restart the other threads + resume_other_threads(); + + // Restore errno and exit (to crash) + errno = old_err; +} + +char addr_buf[18]; +char timeout_buf[22]; +char limit_buf[22]; +char top_buf[22]; +const char *backtracer_argv[] = { + "swift-backtrace", // 0 + "--unwind", // 1 + "precise", // 2 + "--demangle", // 3 + "true", // 4 + "--interactive", // 5 + "true", // 6 + "--color", // 7 + "true", // 8 + "--timeout", // 9 + timeout_buf, // 10 + "--preset", // 11 + "friendly", // 12 + "--crashinfo", // 13 + addr_buf, // 14 + "--threads", // 15 + "preset", // 16 + "--registers", // 17 + "preset", // 18 + "--images", // 19 + "preset", // 20 + "--limit", // 21 + limit_buf, // 22 + "--top", // 23 + top_buf, // 24 + "--sanitize", // 25 + "preset", // 26 + NULL +}; + +// We can't call sprintf() here because we're in a signal handler, +// so we need to be async-signal-safe. +void +format_address(uintptr_t addr, char buffer[18]) +{ + char *ptr = buffer + 18; + *--ptr = '\0'; + while (ptr > buffer) { + char digit = '0' + (addr & 0xf); + if (digit > '9') + digit += 'a' - '0' - 10; + *--ptr = digit; + addr >>= 4; + if (!addr) + break; + } + + // Left-justify in the buffer + if (ptr > buffer) { + char *pt2 = buffer; + while (*ptr) + *pt2++ = *ptr++; + *pt2++ = '\0'; + } +} +void +format_address(const void *ptr, char buffer[18]) +{ + format_address(reinterpret_cast(ptr), buffer); +} + +// See above; we can't use sprintf() here. +void +format_unsigned(unsigned u, char buffer[22]) +{ + char *ptr = buffer + 22; + *--ptr = '\0'; + while (ptr > buffer) { + char digit = '0' + (u % 10); + *--ptr = digit; + u /= 10; + if (!u) + break; + } + + // Left-justify in the buffer + if (ptr > buffer) { + char *pt2 = buffer; + while (*ptr) + *pt2++ = *ptr++; + *pt2++ = '\0'; + } +} + +const char * +trueOrFalse(bool b) { + return b ? "true" : "false"; +} + +const char * +trueOrFalse(OnOffTty oot) { + return trueOrFalse(oot == OnOffTty::On); +} + +bool +run_backtracer() +{ + // Forward our task port to the backtracer; we use the same technique that + // libxpc uses to forward one of its ports on fork(), except that we aren't + // going to call fork() so libxpc's atfork handler won't run and we'll get + // to send the task port to the child. + // + // I would very much like to send a task *read* port, but for some reason + // that doesn't work here. As a result, what we do instead is send the + // control port but have the backtracer use it to get the read port and + // immediately drop the control port. + // + // That *should* be safe enough in practice; if someone could replace the + // backtracer, then they can also replace libswiftCore, and since we do + // this early on in backtracer start-up, the control port won't be valid + // by the time anyone gets to try anything nefarious. + mach_port_t ports[] = { + mach_task_self(), + }; + + mach_ports_register(mach_task_self(), ports, 1); + + // Set-up the backtracer's command line arguments + switch (_swift_backtraceSettings.algorithm) { + case UnwindAlgorithm::Fast: + backtracer_argv[2] = "fast"; + break; + default: + backtracer_argv[2] = "precise"; + break; + } + + // (The TTY option has already been handled at this point, so these are + // all either "On" or "Off".) + backtracer_argv[4] = trueOrFalse(_swift_backtraceSettings.demangle); + backtracer_argv[6] = trueOrFalse(_swift_backtraceSettings.interactive); + backtracer_argv[8] = trueOrFalse(_swift_backtraceSettings.color); + + switch (_swift_backtraceSettings.threads) { + case ThreadsToShow::Preset: + backtracer_argv[16] = "preset"; + break; + case ThreadsToShow::All: + backtracer_argv[16] = "all"; + break; + case ThreadsToShow::Crashed: + backtracer_argv[16] = "crashed"; + break; + } + + switch (_swift_backtraceSettings.registers) { + case RegistersToShow::Preset: + backtracer_argv[18] = "preset"; + break; + case RegistersToShow::None: + backtracer_argv[18] = "none"; + break; + case RegistersToShow::All: + backtracer_argv[18] = "all"; + break; + case RegistersToShow::Crashed: + backtracer_argv[18] = "crashed"; + break; + } + + switch (_swift_backtraceSettings.images) { + case ImagesToShow::Preset: + backtracer_argv[20] = "preset"; + break; + case ImagesToShow::None: + backtracer_argv[20] = "none"; + break; + case ImagesToShow::All: + backtracer_argv[20] = "all"; + break; + case ImagesToShow::Mentioned: + backtracer_argv[20] = "mentioned"; + break; + } + + switch (_swift_backtraceSettings.preset) { + case Preset::Friendly: + backtracer_argv[12] = "friendly"; + break; + case Preset::Medium: + backtracer_argv[12] = "medium"; + break; + default: + backtracer_argv[12] = "full"; + break; + } + + switch (_swift_backtraceSettings.sanitize) { + case SanitizePaths::Preset: + backtracer_argv[26] = "preset"; + break; + case SanitizePaths::Off: + backtracer_argv[26] = "false"; + break; + case SanitizePaths::On: + backtracer_argv[26] = "true"; + break; + } + + format_unsigned(_swift_backtraceSettings.timeout, timeout_buf); + + if (_swift_backtraceSettings.limit < 0) + std::strcpy(limit_buf, "none"); + else + format_unsigned(_swift_backtraceSettings.limit, limit_buf); + + format_unsigned(_swift_backtraceSettings.top, top_buf); + format_address(&crashInfo, addr_buf); + + // Actually execute it + return _swift_spawnBacktracer(backtracer_argv); +} + +} // namespace + +#endif // TARGET_OS_OSX + +#endif // __APPLE__ + diff --git a/stdlib/public/runtime/EnvironmentVariables.def b/stdlib/public/runtime/EnvironmentVariables.def index 48b85a762f48d..1bf01ec7415af 100644 --- a/stdlib/public/runtime/EnvironmentVariables.def +++ b/stdlib/public/runtime/EnvironmentVariables.def @@ -86,4 +86,9 @@ VARIABLE(SWIFT_ROOT, string, "", "This is used to locate auxiliary files relative to the runtime " "itself.") +VARIABLE(SWIFT_BACKTRACE, string, "", + "A comma-separated list of key=value pairs that controls the " + "crash catching and backtracing support in the runtime. " + "See docs/Backtracing.rst in the Swift repository for details.") + #undef VARIABLE From eeb5eea45f7840566beb7dd7207357d9da526a7b Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Tue, 14 Feb 2023 15:19:22 +0000 Subject: [PATCH 02/22] [Backtracing] Needed to add some code to this header for the runtime. In order for the runtime PR to work as a separate PR, it does need a little bit of code in the `_SwiftBacktracing.h` header in SwiftShims. rdar://105391747 --- .../swift/shims/_SwiftBacktracing.h | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 stdlib/public/SwiftShims/swift/shims/_SwiftBacktracing.h diff --git a/stdlib/public/SwiftShims/swift/shims/_SwiftBacktracing.h b/stdlib/public/SwiftShims/swift/shims/_SwiftBacktracing.h new file mode 100644 index 0000000000000..46f1e0833690d --- /dev/null +++ b/stdlib/public/SwiftShims/swift/shims/_SwiftBacktracing.h @@ -0,0 +1,39 @@ +//===--- _SwiftBacktracing.h - Swift Backtracing Support --------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// +// +// Defines types and support functions for the Swift backtracing code. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_BACKTRACING_H +#define SWIFT_BACKTRACING_H + +#include + +#ifdef __cplusplus +namespace swift { +extern "C" { +#endif + +struct CrashInfo { + uint64_t crashing_thread; + uint64_t signal; + uint64_t fault_address; + uint64_t mctx; +}; + +#ifdef __cplusplus +} // extern "C" +} // namespace swift +#endif + +#endif // SWIFT_BACKTRACING_H From b06d890d0cd8a416b6b20caba4b057e6dfd3b30a Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Tue, 14 Feb 2023 15:21:01 +0000 Subject: [PATCH 03/22] [Backtracing][Runtime] Disable backtraces for setuid binaries. We really, really shouldn't be running the external backtracer for setuid binaries. It's just too dangerous. So don't do that. And if someone tries to force us, emit a warning. rdar://105391747 --- stdlib/public/runtime/Backtrace.cpp | 33 +++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/stdlib/public/runtime/Backtrace.cpp b/stdlib/public/runtime/Backtrace.cpp index a1bcad306e1ea..4ecf7f2c60ce2 100644 --- a/stdlib/public/runtime/Backtrace.cpp +++ b/stdlib/public/runtime/Backtrace.cpp @@ -217,15 +217,20 @@ const char *presetToString(Preset preset) { BacktraceInitializer::BacktraceInitializer() { const char *backtracing = swift::runtime::environment::SWIFT_BACKTRACE(); +#if !_WIN32 + // Force off for setuid processes. + if (issetugid()) { + _swift_backtraceSettings.enabled = OnOffTty::Off; + } +#endif + if (backtracing) _swift_parseBacktracingSettings(backtracing); #if TARGET_OS_OSX - // Make sure that we don't pass on setuid privileges, and that all fds - // are closed except for stdin/stdout/stderr. + // Make sure that all fds are closed except for stdin/stdout/stderr. posix_spawnattr_init(&backtraceSpawnAttrs); - posix_spawnattr_setflags(&backtraceSpawnAttrs, - POSIX_SPAWN_RESETIDS | POSIX_SPAWN_CLOEXEC_DEFAULT); + posix_spawnattr_setflags(&backtraceSpawnAttrs, POSIX_SPAWN_CLOEXEC_DEFAULT); posix_spawn_file_actions_init(&backtraceFileActions); posix_spawn_file_actions_addinherit_np(&backtraceFileActions, STDIN_FILENO); @@ -241,6 +246,26 @@ BacktraceInitializer::BacktraceInitializer() { _swift_backtraceSettings.enabled = OnOffTty::Off; } #else + #if !_WIN32 + if (issetugid()) { + if (_swift_backtraceSettings.enabled != OnOffTty::Off) { + // You'll only see this warning if you do e.g. + // + // SWIFT_BACKTRACE=enable=on /path/to/some/setuid/binary + // + // as opposed to + // + // /path/to/some/setuid/binary + // + // i.e. when you're trying to force matters. + swift::warning(0, + "swift runtime: backtrace-on-crash is not supported for " + "setuid executables.\n"); + _swift_backtraceSettings.enabled = OnOffTty::Off; + } + } + #endif // !_WIN32 + if (_swift_backtraceSettings.enabled == OnOffTty::TTY) _swift_backtraceSettings.enabled = isStdoutATty() ? OnOffTty::On : OnOffTty::Off; From 5014e59574da8e917b18559cdcfc1be18992a18e Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Wed, 15 Feb 2023 16:35:02 +0000 Subject: [PATCH 04/22] [Backtracing][Runtime] Don't replace existing signal handlers. If we find signal handlers already installed, leave them alone. rdar://105391747 --- stdlib/public/runtime/CrashHandlerMacOS.cpp | 36 ++++++++++++++------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/stdlib/public/runtime/CrashHandlerMacOS.cpp b/stdlib/public/runtime/CrashHandlerMacOS.cpp index ce2fe711a2cfa..382d59d6eb773 100644 --- a/stdlib/public/runtime/CrashHandlerMacOS.cpp +++ b/stdlib/public/runtime/CrashHandlerMacOS.cpp @@ -82,16 +82,22 @@ _swift_installCrashHandler() { stack_t ss; - // Install an alternate signal handling stack - ss.ss_flags = 0; - ss.ss_size = SIGSTKSZ; - ss.ss_sp = mmap(0, ss.ss_size, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - if (ss.ss_sp == MAP_FAILED) + // See if an alternate signal stack already exists + if (sigaltstack(NULL, &ss) < 0) return errno; - if (sigaltstack(&ss, 0) < 0) - return errno; + if (ss.ss_sp == 0) { + // No, so set one up + ss.ss_flags = 0; + ss.ss_size = SIGSTKSZ; + ss.ss_sp = mmap(0, ss.ss_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (ss.ss_sp == MAP_FAILED) + return errno; + + if (sigaltstack(&ss, NULL) < 0) + return errno; + } // Now register signal handlers struct sigaction sa; @@ -102,13 +108,21 @@ _swift_installCrashHandler() } sa.sa_handler = NULL; + sa.sa_flags = SA_ONSTACK | SA_SIGINFO | SA_NODEFER; + sa.sa_sigaction = handle_fatal_signal; for (unsigned n = 0; n < lengthof(signalsToHandle); ++n) { - sa.sa_flags = SA_ONSTACK | SA_SIGINFO | SA_NODEFER; - sa.sa_sigaction = handle_fatal_signal; + struct sigaction osa; - if (sigaction(signalsToHandle[n], &sa, NULL) < 0) + // See if a signal handler for this signal is already installed + if (sigaction(signalsToHandle[n], NULL, &osa) < 0) return errno; + + if (osa.sa_handler == SIG_DFL) { + // No, so install ours + if (sigaction(signalsToHandle[n], &sa, NULL) < 0) + return errno; + } } return 0; From c76647f92cdac9aa9f933eae76c7eefad70726f4 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Thu, 2 Mar 2023 09:36:08 +0000 Subject: [PATCH 05/22] [Backtracing][Runtime] Renamed getAuxiliaryExecutablePath upstream. Need to change this to `swift_copyAuxiliaryExecutablePath()`. rdar://105391747 --- stdlib/public/runtime/Backtrace.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/public/runtime/Backtrace.cpp b/stdlib/public/runtime/Backtrace.cpp index 4ecf7f2c60ce2..4a42d51d45412 100644 --- a/stdlib/public/runtime/Backtrace.cpp +++ b/stdlib/public/runtime/Backtrace.cpp @@ -289,7 +289,7 @@ BacktraceInitializer::BacktraceInitializer() { if (_swift_backtraceSettings.enabled == OnOffTty::On && !_swift_backtraceSettings.swiftBacktracePath) { _swift_backtraceSettings.swiftBacktracePath - = swift_getAuxiliaryExecutablePath("swift-backtrace"); + = swift_copyAuxiliaryExecutablePath("swift-backtrace"); if (!_swift_backtraceSettings.swiftBacktracePath) { swift::warning(0, From 3fd6fdb12a2767cf393150070acd36fbd111cbdc Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Mon, 13 Feb 2023 15:13:56 +0000 Subject: [PATCH 06/22] [Backtracing] Add _Backtracing library to the build. Adds a new swift_Backtracing library, with a corresponding _Backtracing module, to the build. Also add some tests. This is not public API at this point, but will be used by the external backtracing program, `swift-backtrace`. rdar://104336548 --- stdlib/public/Backtracing/Backtrace.swift | 496 +++++++++ .../Backtracing/BacktraceFormatter.swift | 983 ++++++++++++++++++ stdlib/public/Backtracing/CMakeLists.txt | 53 + stdlib/public/Backtracing/Context.swift | 865 +++++++++++++++ .../Backtracing/CoreSymbolication.swift | 360 +++++++ .../Backtracing/FramePointerUnwinder.swift | 152 +++ stdlib/public/Backtracing/MemoryReader.swift | 118 +++ stdlib/public/Backtracing/Registers.swift | 586 +++++++++++ .../Backtracing/SymbolicatedBacktrace.swift | 464 +++++++++ stdlib/public/Backtracing/Utils.swift | 32 + stdlib/public/Backtracing/get-cpu-context.S | 142 +++ stdlib/public/CMakeLists.txt | 2 + .../SwiftShims/swift/shims/CMakeLists.txt | 1 + .../swift/shims/_SwiftBacktracing.h | 229 ++++ .../SwiftShims/swift/shims/module.modulemap | 4 + test/Backtracing/BacktraceWithLimit.swift | 45 + .../BacktraceWithLimitAndTop.swift | 60 ++ test/Backtracing/SimpleAsyncBacktrace.swift | 54 + test/Backtracing/SimpleBacktrace.swift | 45 + test/lit.cfg | 10 +- 20 files changed, 4700 insertions(+), 1 deletion(-) create mode 100644 stdlib/public/Backtracing/Backtrace.swift create mode 100644 stdlib/public/Backtracing/BacktraceFormatter.swift create mode 100644 stdlib/public/Backtracing/CMakeLists.txt create mode 100644 stdlib/public/Backtracing/Context.swift create mode 100644 stdlib/public/Backtracing/CoreSymbolication.swift create mode 100644 stdlib/public/Backtracing/FramePointerUnwinder.swift create mode 100644 stdlib/public/Backtracing/MemoryReader.swift create mode 100644 stdlib/public/Backtracing/Registers.swift create mode 100644 stdlib/public/Backtracing/SymbolicatedBacktrace.swift create mode 100644 stdlib/public/Backtracing/Utils.swift create mode 100644 stdlib/public/Backtracing/get-cpu-context.S create mode 100644 test/Backtracing/BacktraceWithLimit.swift create mode 100644 test/Backtracing/BacktraceWithLimitAndTop.swift create mode 100644 test/Backtracing/SimpleAsyncBacktrace.swift create mode 100644 test/Backtracing/SimpleBacktrace.swift diff --git a/stdlib/public/Backtracing/Backtrace.swift b/stdlib/public/Backtracing/Backtrace.swift new file mode 100644 index 0000000000000..dbeaeb10057c0 --- /dev/null +++ b/stdlib/public/Backtracing/Backtrace.swift @@ -0,0 +1,496 @@ +//===--- Backtrace.swift --------------------------------------*- swift -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// +// +// Defines the `Backtrace` struct that represents a captured backtrace. +// +//===----------------------------------------------------------------------===// + +import Swift + +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + +@_implementationOnly import Darwin.Mach +@_implementationOnly import _SwiftBacktracingShims + +#endif + +/// Holds a backtrace. +public struct Backtrace: CustomStringConvertible, Sendable { + /// The type of an address. + /// + /// This will be `UInt32` or `UInt64` on current platforms. + #if arch(x86_64) || arch(arm64) + public typealias Address = UInt64 + #elseif arch(i386) || arch(arm) + public typealias Address = UInt32 + #else + #error("You need to fill this in for your architecture.") + #endif + + /// The unwind algorithm to use. + public enum UnwindAlgorithm { + /// Choose the most appropriate for the platform. + case auto + + /// Use the fastest viable method. + /// + /// Typically this means walking the frame pointers. + case fast + + /// Use the most precise available method. + /// + /// On Darwin and on ELF platforms, this will use EH unwind + /// information. On Windows, it will use Win32 API functions. + case precise + } + + /// Represents an individual frame in a backtrace. + public enum Frame: CustomStringConvertible, Sendable { + /// An accurate program counter. + /// + /// This might come from a signal handler, or an exception or some + /// other situation in which we have captured the actual program counter. + case programCounter(Address) + + /// A return address. + /// + /// Corresponds to a normal function call. + case returnAddress(Address) + + /// An async resume point. + /// + /// Corresponds to an `await` in an async task. + case asyncResumePoint(Address) + + /// Indicates a discontinuity in the backtrace. + /// + /// This occurs when you set a limit and a minimum number of frames at + /// the top. For example, if you set a limit of 10 frames and a minimum + /// of 4 top frames, but the backtrace generated 100 frames, you will see + /// + /// 0: frame 100 <----- bottom of call stack + /// 1: frame 99 + /// 2: frame 98 + /// 3: frame 97 + /// 4: frame 96 + /// 5: ... <----- omittedFrames(92) + /// 6: frame 3 + /// 7: frame 2 + /// 8: frame 1 + /// 9: frame 0 <----- top of call stack + /// + /// Note that the limit *includes* the discontinuity. + /// + /// This is good for handling cases involving deep recursion. + case omittedFrames(Int) + + /// Indicates a discontinuity of unknown length. + case truncated + + /// The program counter, without any adjustment. + public var originalProgramCounter: Address { + switch self { + case let .returnAddress(addr): + return addr + case let .programCounter(addr): + return addr + case let .asyncResumePoint(addr): + return addr + case .omittedFrames(_), .truncated: + return 0 + } + } + + /// The adjusted program counter to use for symbolication. + public var adjustedProgramCounter: Address { + switch self { + case let .returnAddress(addr): + return addr - 1 + case let .programCounter(addr): + return addr + case let .asyncResumePoint(addr): + return addr + case .omittedFrames(_), .truncated: + return 0 + } + } + + /// A textual description of this frame. + public var description: String { + switch self { + case let .programCounter(addr): + return "\(hex(addr))" + case let .returnAddress(addr): + return "\(hex(addr)) [ra]" + case let .asyncResumePoint(addr): + return "\(hex(addr)) [async]" + case .omittedFrames(_), .truncated: + return "..." + } + } + } + + /// Represents an image loaded in the process's address space + public struct Image: CustomStringConvertible, Sendable { + /// The name of the image (e.g. libswiftCore.dylib). + public var name: String + + /// The full path to the image (e.g. /usr/lib/swift/libswiftCore.dylib). + public var path: String + + /// The build ID of the image, as a byte array (note that the exact number + /// of bytes may vary, and that some images may not have a build ID). + public var buildID: [UInt8]? + + /// The base address of the image. + public var baseAddress: Backtrace.Address + + /// The end of the text segment in this image. + public var endOfText: Backtrace.Address + + /// Provide a textual description of an Image. + public var description: String { + if let buildID = self.buildID { + return "\(hex(baseAddress))-\(hex(endOfText)) \(hex(buildID)) \(name) \(path)" + } else { + return "\(hex(baseAddress))-\(hex(endOfText)) \(name) \(path)" + } + } + } + + /// A list of captured frame information. + public var frames: [Frame] + + /// A list of captured images. + /// + /// Some backtracing algorithms may require this information, in which case + /// it will be filled in by the `capture()` method. Other algorithms may + /// not, in which case it will be empty and you can capture an image list + /// separately yourself using `captureImages()`. + public var images: [Image]? + + /// Holds information about the shared cache. + public struct SharedCacheInfo: Sendable { + #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + /// The UUID from the shared cache. + public var uuid: [UInt8] + + /// The base address of the shared cache. + public var baseAddress: Backtrace.Address + + /// Says whether there is in fact a shared cache. + public var noCache: Bool + #endif + } + + /// Information about the shared cache. + /// + /// Holds information about the shared cache. On Darwin only, this is + /// required for symbolication. On non-Darwin platforms it will always + /// be `nil`. + public var sharedCacheInfo: SharedCacheInfo? + + /// Capture a backtrace from the current program location. + /// + /// The `capture()` method itself will not be included in the backtrace; + /// i.e. the first frame will be the one in which `capture()` was called, + /// and its programCounter value will be the return address for the + /// `capture()` method call. + /// + /// @param algorithm Specifies which unwind mechanism to use. If this + /// is set to `.auto`, we will use the platform default. + /// @param limit The backtrace will include at most this number of + /// frames; you can set this to `nil` to remove the + /// limit completely if required. + /// @param offset Says how many frames to skip; this makes it easy to + /// wrap this API without having to inline things and + /// without including unnecessary frames in the backtrace. + /// @param top Sets the minimum number of frames to capture at the + /// top of the stack. + /// + /// @returns A new `Backtrace` struct. + @inline(never) + public static func capture(algorithm: UnwindAlgorithm = .auto, + limit: Int? = 64, + offset: Int = 0, + top: Int = 16) throws -> Backtrace { + // N.B. We use offset+1 here to skip this frame, rather than inlining + // this code into the client. + return try HostContext.withCurrentContext { ctx in + try capture(from: ctx, + using: UnsafeLocalMemoryReader(), + algorithm: algorithm, + limit: limit, + offset: offset + 1, + top: top) + } + } + + @_spi(Internal) + public static func capture(from context: some Context, + using memoryReader: some MemoryReader, + algorithm: UnwindAlgorithm = .auto, + limit: Int? = 64, + offset: Int = 0, + top: Int = 16) throws -> Backtrace { + switch algorithm { + // All of them, for now, use the frame pointer unwinder. In the long + // run, we should be using DWARF EH frame data for .precise. + case .auto, .fast, .precise: + let unwinder = + FramePointerUnwinder(context: context, memoryReader: memoryReader) + .dropFirst(offset) + + if let limit = limit { + if limit == 0 { + return Backtrace(frames: [.truncated]) + } + + let realTop = top < limit ? top : limit - 1 + var iterator = unwinder.makeIterator() + var frames: [Frame] = [] + + // Capture frames normally until we hit limit + while let frame = iterator.next() { + if frames.count < limit { + frames.append(frame) + if frames.count == limit { + break + } + } + } + + if realTop == 0 { + if let _ = iterator.next() { + // More frames than we were asked for; replace the last + // one with a discontinuity + frames[limit - 1] = .truncated + } + + return Backtrace(frames: frames) + } else { + + // If we still have frames at this point, start tracking the + // last `realTop` frames in a circular buffer. + if let frame = iterator.next() { + let topSection = limit - realTop + var topFrames: [Frame] = [] + var topNdx = 0 + var omittedFrames = 0 + + topFrames.reserveCapacity(realTop) + topFrames.insert(contentsOf: frames.suffix(realTop - 1), at: 0) + topFrames.append(frame) + + while let frame = iterator.next() { + topFrames[topNdx] = frame + topNdx += 1 + omittedFrames += 1 + if topNdx >= realTop { + topNdx = 0 + } + } + + // Fix the backtrace to include a discontinuity followed by + // the contents of the circular buffer. + let firstPart = realTop - topNdx + let secondPart = topNdx + frames[topSection - 1] = .omittedFrames(omittedFrames) + + frames.replaceSubrange(topSection..<(topSection+firstPart), + with: topFrames.suffix(firstPart)) + frames.replaceSubrange((topSection+firstPart).. [Image] { + #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + return captureImages(for: mach_task_self_) + #else + return [] + #endif + } + + #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + private static func withDyldProcessInfo(for task: task_t, + fn: (OpaquePointer?) throws -> T) + rethrows -> T { + var kret: kern_return_t = KERN_SUCCESS + let dyldInfo = _dyld_process_info_create(task, 0, &kret) + + if kret != KERN_SUCCESS { + fatalError("error: cannot create dyld process info") + } + + defer { + _dyld_process_info_release(dyldInfo) + } + + return try fn(dyldInfo) + } + #endif + + @_spi(Internal) + public static func captureImages(for process: Any) -> [Image] { + var images: [Image] = [] + + #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + let task = process as! task_t + + withDyldProcessInfo(for: task){ dyldInfo in + _dyld_process_info_for_each_image(dyldInfo) { + (machHeaderAddress, uuid, path) in + + if let path = path, let uuid = uuid { + let pathString = String(cString: path) + let theUUID = Array(UnsafeBufferPointer(start: uuid, + count: MemoryLayout.size)) + let name: String + if let slashIndex = pathString.lastIndex(of: "/") { + name = String(pathString.suffix(from: + pathString.index(after:slashIndex))) + } else { + name = pathString + } + + // Find the end of the __TEXT segment + var endOfText = machHeaderAddress + 4096 + + _dyld_process_info_for_each_segment(dyldInfo, machHeaderAddress){ + address, size, name in + + if let name = String(validatingUTF8: name!), name == "__TEXT" { + endOfText = address + size + } + } + + images.append(Image(name: name, + path: pathString, + buildID: theUUID, + baseAddress: machHeaderAddress, + endOfText: endOfText)) + } + } + } + #endif // os(macOS) || os(iOS) || os(watchOS) + + return images.sorted(by: { $0.baseAddress < $1.baseAddress }) + } + + /// Capture shared cache information. + /// + /// @returns A `SharedCacheInfo`. + public static func captureSharedCacheInfo() -> SharedCacheInfo { + #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + return captureSharedCacheInfo(for: mach_task_self_) + #else + return SharedCacheInfo() + #endif + } + + @_spi(Internal) + public static func captureSharedCacheInfo(for t: Any) -> SharedCacheInfo { + #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + let task = t as! task_t + return withDyldProcessInfo(for: task){ dyldInfo in + var cacheInfo = dyld_process_cache_info() + _dyld_process_info_get_cache(dyldInfo, &cacheInfo) + let theUUID = withUnsafePointer(to: cacheInfo.cacheUUID){ + $0.withMemoryRebound(to: UInt8.self, + capacity: MemoryLayout.size) { + Array(UnsafeBufferPointer(start: $0, + count: MemoryLayout.size)) + } + } + return SharedCacheInfo(uuid: theUUID, + baseAddress: cacheInfo.cacheBaseAddress, + noCache: cacheInfo.noCache) + } + #else // !os(Darwin) + return SharedCacheInfo() + #endif + } + + /// Return a symbolicated version of the backtrace. + /// + /// @param images Specifies the set of images to use for symbolication. + /// If `nil`, the function will look to see if the `Backtrace` + /// has already captured images. If it has, those will be + /// used; otherwise we will capture images at this point. + /// + /// @param sharedCacheInfo Provides information about the location and + /// identity of the shared cache, if applicable. + /// + /// @param showInlineFrames If `true` and we know how on the platform we're + /// running on, add virtual frames to show inline + /// function calls. + /// + /// @returns A new `SymbolicatedBacktrace`. + public func symbolicated(with images: [Image]? = nil, + sharedCacheInfo: SharedCacheInfo? = nil, + showInlineFrames: Bool = true) + -> SymbolicatedBacktrace? { + return SymbolicatedBacktrace.symbolicate(backtrace: self, + images: images, + sharedCacheInfo: sharedCacheInfo, + showInlineFrames: showInlineFrames) + } + + /// Provide a textual version of the backtrace. + public var description: String { + var lines: [String] = [] + + var n = 0 + for frame in frames { + lines.append("\(n)\t\(frame)") + switch frame { + case let .omittedFrames(count): + n += count + default: + n += 1 + } + } + + if let images = images { + lines.append("") + lines.append("Images:") + lines.append("") + for (n, image) in images.enumerated() { + lines.append("\(n)\t\(image)") + } + } + + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + if let sharedCacheInfo = sharedCacheInfo { + lines.append("") + lines.append("Shared Cache:") + lines.append("") + lines.append(" UUID: \(hex(sharedCacheInfo.uuid))") + lines.append(" Base: \(hex(sharedCacheInfo.baseAddress))") + } + #endif + + return lines.joined(separator: "\n") + } +} diff --git a/stdlib/public/Backtracing/BacktraceFormatter.swift b/stdlib/public/Backtracing/BacktraceFormatter.swift new file mode 100644 index 0000000000000..8c56a551d8799 --- /dev/null +++ b/stdlib/public/Backtracing/BacktraceFormatter.swift @@ -0,0 +1,983 @@ +//===--- BacktraceFormatter.swift -----------------------------*- swift -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// +// +// Provides functionality to format backtraces, with various additional +// options. +// +//===----------------------------------------------------------------------===// + +import Swift + +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) +@_implementationOnly import Darwin +#elseif os(Windows) +@_implementationOnly import MSVCRT +#elseif os(Linux) +@_implementationOnly import Glibc +#endif + +@_implementationOnly import _SwiftBacktracingShims + +/// A backtrace formatting theme. +@_spi(Formatting) +public protocol BacktraceFormattingTheme { + func frameIndex(_ s: String) -> String + func programCounter(_ s: String) -> String + func frameAttribute(_ s: String) -> String + func symbol(_ s: String) -> String + func offset(_ s: String) -> String + func sourceLocation(_ s: String) -> String + func lineNumber(_ s: String) -> String + func code(_ s: String) -> String + func crashedLine(_ s: String) -> String + func crashLocation(_ s: String) -> String + func imageName(_ s: String) -> String + func imageAddressRange(_ s: String) -> String + func imageBuildID(_ s: String) -> String + func imagePath(_ s: String) -> String +} + +extension BacktraceFormattingTheme { + public func frameIndex(_ s: String) -> String { return s } + public func programCounter(_ s: String) -> String { return s } + public func frameAttribute(_ s: String) -> String { return "[\(s)]" } + public func symbol(_ s: String) -> String { return s } + public func offset(_ s: String) -> String { return s } + public func sourceLocation(_ s: String) -> String { return s } + public func lineNumber(_ s: String) -> String { return s } + public func code(_ s: String) -> String { return s } + public func crashedLine(_ s: String) -> String { return s } + public func crashLocation(_ s: String) -> String { return s } + public func imageName(_ s: String) -> String { return s } + public func imageAddressRange(_ s: String) -> String { return s } + public func imageBuildID(_ s: String) -> String { return s } + public func imagePath(_ s: String) -> String { return s} +} + +/// Options for backtrace formatting. +/// +/// This is used by chaining modifiers, e.g. .theme(.color).showSourceCode(). +@_spi(Formatting) +public struct BacktraceFormattingOptions { + var _theme: BacktraceFormattingTheme = BacktraceFormatter.Themes.plain + var _showSourceCode: Bool = false + var _sourceContextLines: Int = 2 + var _showAddresses: Bool = true + var _showImages: ImagesToShow = .mentioned + var _showImageNames: Bool = true + var _showFrameAttributes: Bool = true + var _skipRuntimeFailures: Bool = false + var _skipThunkFunctions: Bool = true + var _skipSystemFrames: Bool = true + var _sanitizePaths: Bool = true + var _demangle: Bool = true + var _width: Int = 80 + + public var selectedTheme: BacktraceFormattingTheme { return _theme } + public var shouldShowSourceCode: Bool { return _showSourceCode } + public var sourceContextLines: Int { return _sourceContextLines } + public var shouldShowAddresses: Bool { return _showAddresses } + public var imagesToShow: ImagesToShow { return _showImages } + public var shouldShowImageNames: Bool { return _showImageNames } + public var shouldShowFrameAttributes: Bool { return _showFrameAttributes } + public var shouldSkipRuntimeFailures: Bool { return _skipRuntimeFailures } + public var shouldSkipThunkFunctions: Bool { return _skipThunkFunctions } + public var shouldSkipSystemFrames: Bool { return _skipSystemFrames } + public var shouldSanitizePaths: Bool { return _sanitizePaths } + public var shouldDemangle: Bool { return _demangle } + public var formattingWidth: Int { return _width } + + public init() {} + + /// Theme to use for formatting. + /// + /// @param theme A `BacktraceFormattingTheme` structure. + /// + /// @returns A new `BacktraceFormattingOptions` structure. + public static func theme(_ theme: BacktraceFormattingTheme) -> BacktraceFormattingOptions { + return BacktraceFormattingOptions().theme(theme) + } + public func theme(_ theme: BacktraceFormattingTheme) -> BacktraceFormattingOptions { + var newOptions = self + newOptions._theme = theme + return newOptions + } + + /// Enable or disable the display of source code in the backtrace. + /// + /// @param enabled Whether or not to enable source code. + /// + /// @param contextLines The number of lines of context either side of the + /// line associated with the backtrace frame. + /// + /// @returns A new `BacktraceFormattingOptions` structure. + public static func showSourceCode(_ enabled: Bool = true, contextLines: Int = 2) -> BacktraceFormattingOptions { + return BacktraceFormattingOptions().showSourceCode(enabled, + contextLines: contextLines) + } + public func showSourceCode(_ enabled: Bool = true, contextLines: Int = 2) -> BacktraceFormattingOptions { + var newOptions = self + newOptions._showSourceCode = enabled + newOptions._sourceContextLines = contextLines + return newOptions + } + + /// Enable or disable the display of raw addresses. + /// + /// @param enabled If false, we will only display a raw address in the + /// backtrace if we haven't been able to symbolicate. + /// + /// @returns A new `BacktraceFormattingOptions` structure. + public static func showAddresses(_ enabled: Bool = true) -> BacktraceFormattingOptions { + return BacktraceFormattingOptions().showAddresses(enabled) + } + public func showAddresses(_ enabled: Bool = true) -> BacktraceFormattingOptions { + var newOptions = self + newOptions._showAddresses = enabled + return newOptions + } + + /// Enable or disable the display of the image list. + /// + /// @param enabled Says whether or not to output the image list. + /// + /// @returns A new `BacktraceFormattingOptions` structure. + public enum ImagesToShow { + case none + case mentioned + case all + } + public static func showImages(_ toShow: ImagesToShow = .all) -> BacktraceFormattingOptions { + return BacktraceFormattingOptions().showImages(toShow) + } + public func showImages(_ toShow: ImagesToShow = .all) -> BacktraceFormattingOptions { + var newOptions = self + newOptions._showImages = toShow + return newOptions + } + + /// Enable or disable the display of image names in the frame list. + /// + /// @param enabled If true, we will display the name of the image for + /// each frame. + /// + /// @returns A new `BacktraceFormattingOptions` structure. + public static func showImageNames(_ enabled: Bool = true) -> BacktraceFormattingOptions { + return BacktraceFormattingOptions().showImageNames(enabled) + } + public func showImageNames(_ enabled: Bool = true) -> BacktraceFormattingOptions { + var newOptions = self + newOptions._showImageNames = enabled + return newOptions + } + + /// Enable or disable the display of frame attributes in the frame list. + /// + /// @param enabled If true, we will display the frame attributes. + /// + /// @returns A new `BacktraceFormattingOptions` structure. + public static func showFrameAttributes(_ enabled: Bool = true) -> BacktraceFormattingOptions { + return BacktraceFormattingOptions().showFrameAttributes(enabled) + } + public func showFrameAttributes(_ enabled: Bool = true) -> BacktraceFormattingOptions { + var newOptions = self + newOptions._showFrameAttributes = enabled + return newOptions + } + + /// Set whether or not to show Swift runtime failure frames. + /// + /// @param enabled If true, we will skip Swift runtime failure frames. + /// + /// @returns A new `BacktraceFormattingOptions` structure. + public static func skipRuntimeFailures(_ enabled: Bool = true) -> BacktraceFormattingOptions { + return BacktraceFormattingOptions().skipRuntimeFailures(enabled) + } + public func skipRuntimeFailures(_ enabled: Bool = true) -> BacktraceFormattingOptions { + var newOptions = self + newOptions._skipRuntimeFailures = enabled + return newOptions + } + + /// Set whether or not to show Swift thunk function frames. + /// + /// @param enabled If true, we will skip Swift thunk function frames. + /// + /// @returns A new `BacktraceFormattingOptions` structure. + public static func skipThunkFunctions(_ enabled: Bool = true) -> BacktraceFormattingOptions { + return BacktraceFormattingOptions().skipThunkFunctions(enabled) + } + public func skipThunkFunctions(_ enabled: Bool = true) -> BacktraceFormattingOptions { + var newOptions = self + newOptions._skipThunkFunctions = enabled + return newOptions + } + + /// Set whether or not to show system frames. + /// + /// For instance, on macOS, this will cause us to skip the "start" frame + /// at the very top of the stack. + /// + /// @param enabled If true, we will skip system frames. + /// + /// @returns A new `BacktraceFormattingOptions` structure. + public static func skipSystemFrames(_ enabled: Bool = true) -> BacktraceFormattingOptions { + return BacktraceFormattingOptions().skipSystemFrames(enabled) + } + public func skipSystemFrames(_ enabled: Bool = true) -> BacktraceFormattingOptions { + var newOptions = self + newOptions._skipSystemFrames = enabled + return newOptions + } + + /// Enable or disable path sanitization. + /// + /// This is intended to avoid leaking PII into crash logs. + /// + /// @param enabled If true, paths will be sanitized. + /// + /// @returns A new `BacktraceFormattingOptions` structure. + public static func sanitizePaths(_ enabled: Bool = true) -> BacktraceFormattingOptions { + return BacktraceFormattingOptions().sanitizePaths(enabled) + } + public func sanitizePaths(_ enabled: Bool = true) -> BacktraceFormattingOptions { + var newOptions = self + newOptions._sanitizePaths = enabled + return newOptions + } + + /// Set whether we show mangled or demangled names. + /// + /// @param enabled If true, we show demangled names if we have them. + /// + /// @returns A new `BacktraceFormattingOptions` structure. + public static func demangle(_ enabled: Bool = true) -> BacktraceFormattingOptions { + return BacktraceFormattingOptions().demangle(enabled) + } + public func demangle(_ enabled: Bool = true) -> BacktraceFormattingOptions { + var newOptions = self + newOptions._demangle = enabled + return newOptions + } + + /// Set the output width. + /// + /// @param width The output width in characters. This is only used to + /// highlight information, and defaults to 80. + /// + /// returns A new `BacktraceFormattingOptions` structure. + public static func width(_ width: Int) -> BacktraceFormattingOptions { + return BacktraceFormattingOptions().width(width) + } + public func width(_ width: Int) -> BacktraceFormattingOptions { + var newOptions = self + newOptions._width = width + return newOptions + } +} + +/// Return the width of a given Unicode.Scalar. +/// +/// It would be nice to have the Unicode width data, which would let us do +/// a better job of this. +private func measure(_ ch: Unicode.Scalar) -> Int { + if ch.isASCII { + return 1 + } + + if #available(macOS 10.12.2, *) { + if ch.properties.isEmoji { + return 2 + } + } else if ch.value >= 0x1f100 && ch.value <= 0x1fb00 { + return 2 + } + + if ch.properties.isIdeographic + && !(ch.value >= 0xff61 && ch.value <= 0xffdc) + && !(ch.value >= 0xffe8 && ch.value <= 0xffee) { + return 2 + } + + if ch.properties.canonicalCombiningClass.rawValue != 0 { + return 0 + } + + switch ch.properties.generalCategory { + case .control, .nonspacingMark: + return 0 + default: + return 1 + } +} + +/// Compute the width of the given string, ignoring CSI formatting codes. +private enum MeasureState { + // Normal state + case normal + + // Start of an escape + case escape + + // In a CSI escape + case csi +} + +private func measure(_ s: S) -> Int { + var totalWidth = 0 + var state: MeasureState = .normal + + for ch in s.unicodeScalars { + switch state { + case .normal: + if ch.value == 27 { + // This is an escape sequence + state = .escape + } else { + totalWidth += measure(ch) + } + case .escape: + if ch.value == 0x5b { + state = .csi + } else { + state = .normal + } + case .csi: + if ch.value >= 0x40 && ch.value <= 0x7e { + state = .normal + } + } + } + return totalWidth +} + +/// Pad the given string to the given width using spaces. +private func pad(_ s: String, to width: Int, + aligned alignment: BacktraceFormatter.Alignment = .left) + -> String { + + let currentWidth = measure(s) + let padding = max(width - currentWidth, 0) + + switch alignment { + case .left: + let spaces = String(repeating: " ", count: padding) + + return s + spaces + case .right: + let spaces = String(repeating: " ", count: padding) + + return spaces + s + case .center: + let left = padding / 2 + let right = padding - left + let leftSpaces = String(repeating: " ", count: left) + let rightSpaces = String(repeating: " ", count: right) + + return "\(leftSpaces)\(s)\(rightSpaces)" + } +} + +/// Untabify the given string, assuming tabs of the specified size. +/// +/// @param s The string to untabify. +/// @param tabWidth The tab width to assume (default 8). +/// +/// @returns A string with all the tabs replaced with appropriate numbers +/// of spaces. +private func untabify(_ s: String, tabWidth: Int = 8) -> String { + var result: String = "" + var first = true + for chunk in s.split(separator: "\t", omittingEmptySubsequences: false) { + if first { + first = false + } else { + let toTabStop = tabWidth - measure(result) % tabWidth + result += String(repeating: " ", count: toTabStop) + } + result += chunk + } + return result +} + +/// Sanitize a path to remove usernames, volume names and so on. +/// +/// The point of this function is to try to remove anything that might +/// contain PII before it ends up in a log file somewhere. +/// +/// @param path The path to sanitize. +/// +/// @returns A string containing the sanitized path. +private func sanitizePath(_ path: String) -> String { + #if os(macOS) + return CRCopySanitizedPath(path, + kCRSanitizePathGlobAllTypes + | kCRSanitizePathKeepFile) + #else + // For now, on non-macOS systems, do nothing + return path + #endif +} + +/// Trim whitespace from the right hand end of a string. +/// +/// @param s The string to trim. +/// +/// @returns A string with the whitespace trimmed. +private func rtrim(_ s: S) -> S.SubSequence { + if let lastNonWhitespace = s.lastIndex(where: { !$0.isWhitespace }) { + return s.prefix(through: lastNonWhitespace) + } + return s.dropLast(0) +} + +/// Responsible for formatting backtraces. +@_spi(Formatting) +public struct BacktraceFormatter { + + /// The formatting options to apply when formatting data. + public var options: BacktraceFormattingOptions + + public struct Themes { + /// A plain formatting theme. + public struct PlainTheme: BacktraceFormattingTheme { + } + + public static let plain = PlainTheme() + } + + public init(_ options: BacktraceFormattingOptions) { + self.options = options + } + + public enum TableRow { + case columns([String]) + case raw(String) + } + + public enum Alignment { + case left + case right + case center + } + + /// Output a table with each column nicely aligned. + /// + /// @param rows An array of table rows, each of which holds an array + /// of table columns. + /// + /// @result A `String` containing the formatted table. + public static func formatTable(_ rows: [TableRow], + alignments: [Alignment] = []) -> String { + // Work out how many columns we have + let colCount = rows.map{ + if case let .columns(columns) = $0 { + return columns.count + } else { + return 0 + } + }.reduce(0, max) + + // Now compute their widths + var widths = Array(repeating: 0, count: colCount) + for row in rows { + if case let .columns(columns) = row { + for (n, width) in columns.lazy.map(measure).enumerated() { + widths[n] = max(widths[n], width) + } + } + } + + // Generate lines for the table + var lines: [Substring] = [] + for row in rows { + switch row { + case let .columns(columns): + let line = columns.enumerated().map{ n, column in + let alignment = n < alignments.count ? alignments[n] : .left + if n == colCount - 1 && alignment == .left { + return column + } else { + return pad(column, to: widths[n], aligned: alignment) + } + }.joined(separator: " ") + + lines.append(rtrim(line)) + case let .raw(line): + lines.append(rtrim(line)) + } + } + + // Trim any empty lines from the end + guard let lastNonEmpty = lines.lastIndex(where: { !$0.isEmpty }) else { + return "" + } + + return lines.prefix(through: lastNonEmpty).joined(separator: "\n") + } + + /// Format an individual frame into a list of columns. + /// + /// @param frame The frame to format. + /// @param index The frame index, if required. + /// + /// @result An array of strings, one per column. + public func formatColumns(frame: Backtrace.Frame, + index: Int? = nil) -> [String] { + let pc: String + var attrs: [String] = [] + + switch frame { + case let .programCounter(address): + pc = "\(hex(address))" + case let .returnAddress(address): + pc = "\(hex(address))" + attrs.append("ra") + case let .asyncResumePoint(address): + pc = "\(hex(address))" + attrs.append("async") + case .omittedFrames(_), .truncated: + pc = "..." + } + + var columns: [String] = [] + 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: " ")) + } + + return columns + } + + /// Format a frame into a list of rows. + /// + /// @param frame The frame to format. + /// @param index The frame index, if required. + /// + /// @result An array of table rows. + public func formatRows(frame: Backtrace.Frame, + index: Int? = nil) -> [TableRow] { + return [.columns(formatColumns(frame: frame, index: index))] + } + + /// Format just one frame. + /// + /// @param frame The frame to format. + /// @param index (Optional) frame index. + /// + /// @result A `String` containing the formatted data. + public func format(frame: Backtrace.Frame, index: Int? = nil) -> String { + let rows = formatRows(frame: frame, index: index) + return BacktraceFormatter.formatTable(rows, alignments: [.right]) + } + + /// Format the frame list from a backtrace. + /// + /// @param frames The frames to format. + /// + /// @result A `String` containing the formatted data. + public func format(frames: some Sequence) -> String { + var rows: [TableRow] = [] + + var n = 0 + for frame in frames { + rows += formatRows(frame: frame, index: n) + + if case let .omittedFrames(count) = frame { + n += count + } else { + n += 1 + } + } + + return BacktraceFormatter.formatTable(rows, alignments: [.right]) + } + + /// Format a `Backtrace` + /// + /// @param backtrace The `Backtrace` object to format. + /// + /// @result A `String` containing the formatted data. + public func format(backtrace: Backtrace) -> String { + return format(frames: backtrace.frames) + } + + /// Grab source lines for a symbolicated backtrace. + /// + /// Tries to open the file corresponding to the symbol; if successful, + /// it will return a string containing the specified lines of context, + /// with the point at which the program crashed highlighted. + private func formattedSourceLines(from sourceLocation: SymbolicatedBacktrace.SourceLocation, + indent theIndent: Int = 2) -> String? { + guard let fp = fopen(sourceLocation.path, "rt") else { return nil } + defer { + fclose(fp) + } + + let indent = String(repeating: " ", count: theIndent) + var lines: [String] = [] + var line = 1 + let buffer = UnsafeMutableBufferPointer.allocate(capacity: 4096) + var currentLine = "" + + let maxLine = sourceLocation.line + options._sourceContextLines + let maxLineWidth = max("\(maxLine)".count, 4) + + let doLine = { sourceLine in + if line >= sourceLocation.line - options._sourceContextLines + && line <= sourceLocation.line + options._sourceContextLines { + let untabified = untabify(sourceLine) + let code = options._theme.code(untabified) + let lineNumber = options._theme.lineNumber(pad("\(line)", + to: maxLineWidth, + aligned: .right)) + let theLine: String + if line == sourceLocation.line { + let highlightWidth = options._width - 2 * theIndent + theLine = options._theme.crashedLine(pad("\(lineNumber)│ \(code) ", + to: highlightWidth)) + } else { + theLine = "\(lineNumber)│ \(code)" + } + lines.append("\(indent)\(theLine)") + + if line == sourceLocation.line { + // sourceLocation.column is an index in UTF-8 code units in + // `untabified`. We should point at the grapheme cluster that + // contains that UTF-8 index. + let adjustedColumn = max(sourceLocation.column, 1) + let utf8Ndx + = untabified.utf8.index(untabified.utf8.startIndex, + offsetBy: adjustedColumn, + limitedBy: untabified.utf8.endIndex) + ?? untabified.utf8.endIndex + + // Adjust it to point at a grapheme cluster start + let strNdx = untabified.index( + untabified.index(utf8Ndx, offsetBy: 1, + limitedBy: untabified.endIndex) + ?? untabified.endIndex, + offsetBy: -1, + limitedBy: untabified.startIndex) ?? untabified.startIndex + + // Work out the terminal width up to that point + let terminalWidth = measure(untabified.prefix(upTo: strNdx)) + + let pad = String(repeating: " ", + count: max(terminalWidth - 1, 0)) + + let marker = options._theme.crashLocation("▲") + let blankForNumber = String(repeating: " ", count: maxLineWidth) + + lines.append("\(indent)\(blankForNumber)│ \(pad)\(marker)") + } + } + } + + while feof(fp) == 0 && ferror(fp) == 0 { + guard let result = fgets(buffer.baseAddress, + Int32(buffer.count), fp) else { + break + } + + let chunk = String(cString: result) + currentLine += chunk + if currentLine.hasSuffix("\n") { + currentLine.removeLast() + doLine(currentLine) + currentLine = "" + line += 1 + } + } + + doLine(currentLine) + + return lines.joined(separator: "\n") + } + + /// Format an individual frame into a list of columns. + /// + /// @params frame The frame to format. + /// + /// @result An array of strings, one per column. + public func formatColumns(frame: SymbolicatedBacktrace.Frame, + index: Int? = nil) -> [String] { + let pc: String + var attrs: [String] = [] + + switch frame.captured { + case let .programCounter(address): + pc = "\(hex(address))" + case let .returnAddress(address): + pc = "\(hex(address))" + attrs.append("ra") + case let .asyncResumePoint(address): + pc = "\(hex(address))" + attrs.append("async") + case .omittedFrames(_), .truncated: + pc = "" + } + + if frame.inlined { + attrs.append("inlined") + } + + if frame.isSwiftThunk { + attrs.append("thunk") + } + + if frame.isSystem { + attrs.append("system") + } + + var formattedSymbol: String? = nil + var hasSourceLocation = false + + if let symbol = frame.symbol { + let displayName = options._demangle ? symbol.name : symbol.rawName + let themedName = options._theme.symbol(displayName) + + let offset: String + if symbol.offset > 0 { + offset = options._theme.offset(" + \(symbol.offset)") + } else if symbol.offset < 0 { + offset = options._theme.offset(" - \(-symbol.offset)") + } else { + offset = "" + } + + let imageName: String + if options._showImageNames { + if symbol.imageIndex >= 0 { + imageName = " in " + options._theme.imageName(symbol.imageName) + } else { + imageName = "" + } + } else { + imageName = "" + } + + let location: String + if var sourceLocation = symbol.sourceLocation { + if options._sanitizePaths { + sourceLocation.path = sanitizePath(sourceLocation.path) + } + location = " at " + options._theme.sourceLocation("\(sourceLocation)") + hasSourceLocation = true + } else { + location = "" + } + + formattedSymbol = "\(themedName)\(offset)\(imageName)\(location)" + } + + let location: String + if !hasSourceLocation || options._showAddresses { + let formattedPc = options._theme.programCounter(pc) + if let formattedSymbol = formattedSymbol { + location = "\(formattedPc) \(formattedSymbol)" + } else { + location = formattedPc + } + } else if let formattedSymbol = formattedSymbol { + location = formattedSymbol + } else { + location = options._theme.programCounter(pc) + } + + var columns: [String] = [] + + if let index = index { + let frameIndex: String + switch frame.captured { + case .omittedFrames(_), .truncated: + frameIndex = options._theme.frameIndex("...") + default: + frameIndex = options._theme.frameIndex("\(index)") + } + columns.append(frameIndex) + } + + if options._showFrameAttributes { + columns.append(attrs.map( + options._theme.frameAttribute + ).joined(separator: " ")) + } + + columns.append(location) + + return columns + } + + /// Format a frame into a list of rows. + /// + /// @param frame The frame to format. + /// @param index The frame index, if required. + /// + /// @result An array of table rows. + public func formatRows(frame: SymbolicatedBacktrace.Frame, + index: Int? = nil, + showSource: Bool = true) -> [TableRow] { + let columns = formatColumns(frame: frame, index: index) + var rows: [TableRow] = [.columns(columns)] + + if showSource { + if let symbol = frame.symbol, + let sourceLocation = symbol.sourceLocation, + let lines = formattedSourceLines(from: sourceLocation) { + rows.append(.raw("")) + rows.append(.raw(lines)) + rows.append(.raw("")) + } + } + + return rows + } + + /// Format just one frame. + /// + /// @param frame The frame to format. + /// @param index (Optional) frame index. + /// + /// @result A `String` containing the formatted data. + public func format(frame: SymbolicatedBacktrace.Frame, + index: Int? = nil, + showSource: Bool = true) -> String { + let rows = formatRows(frame: frame, index: index, showSource: showSource) + return BacktraceFormatter.formatTable(rows, alignments: [.right]) + } + + /// Return `true` if we should skip the specified frame + public func shouldSkip(_ frame: SymbolicatedBacktrace.Frame) -> Bool { + return (options._skipRuntimeFailures && frame.isSwiftRuntimeFailure) + || (options._skipSystemFrames && frame.isSystem) + || (options._skipThunkFunctions && frame.isSwiftThunk) + } + + /// Format the frame list from a symbolicated backtrace. + /// + /// @param frames The frames to format. + /// + /// @result A `String` containing the formatted data. + public func format(frames: some Sequence) -> String { + var rows: [TableRow] = [] + var sourceLocationsShown = Set() + + var n = 0 + for frame in frames { + if shouldSkip(frame) { + continue + } + + var showSource = options._showSourceCode + if let symbol = frame.symbol, + let sourceLocation = symbol.sourceLocation { + if sourceLocationsShown.contains(sourceLocation) { + showSource = false + } else { + sourceLocationsShown.insert(sourceLocation) + } + } + + rows += formatRows(frame: frame, index: n, showSource: showSource) + + if case let .omittedFrames(count) = frame.captured { + n += count + } else { + n += 1 + } + } + + return BacktraceFormatter.formatTable(rows, alignments: [.right]) + } + + /// Format a `SymbolicatedBacktrace` + /// + /// @param backtrace The `SymbolicatedBacktrace` object to format. + /// + /// @result A `String` containing the formatted data. + public func format(backtrace: SymbolicatedBacktrace) -> String { + var result = format(frames: backtrace.frames) + + switch options._showImages { + case .none: + break + case .all: + result += "\n\nImages:\n" + result += format(images: backtrace.images) + case .mentioned: + var mentionedImages = Set() + for frame in backtrace.frames { + if shouldSkip(frame) { + continue + } + if let symbol = frame.symbol, symbol.imageIndex >= 0 { + mentionedImages.insert(symbol.imageIndex) + } + } + + let images = mentionedImages.sorted().map{ backtrace.images[$0] } + let omitted = backtrace.images.count - images.count + if omitted > 0 { + result += "\n\nImages (\(omitted) omitted):\n" + } else { + result += "\n\nImages (only mentioned):\n" + } + result += format(images: images) + } + + return result + } + + /// Format a `Backtrace.Image` into a list of columns. + /// + /// @param image The `Image` object to format. + /// + /// @result An array of strings, one per column. + public func formatColumns(image: Backtrace.Image) -> [String] { + let addressRange = "\(hex(image.baseAddress))–\(hex(image.endOfText))" + let buildID: String + if let bytes = image.buildID { + buildID = hex(bytes) + } else { + buildID = "" + } + let imagePath: String + if options._sanitizePaths { + imagePath = sanitizePath(image.path) + } else { + imagePath = image.path + } + return [ + options._theme.imageAddressRange(addressRange), + options._theme.imageBuildID(buildID), + options._theme.imageName(image.name), + options._theme.imagePath(imagePath) + ] + } + + /// Format an array of `Backtrace.Image`s. + /// + /// @param images The array of `Image` objects to format. + /// + /// @result A string containing the formatted data. + public func format(images: some Sequence) -> String { + let rows = images.map{ TableRow.columns(formatColumns(image: $0)) } + + return BacktraceFormatter.formatTable(rows) + } +} diff --git a/stdlib/public/Backtracing/CMakeLists.txt b/stdlib/public/Backtracing/CMakeLists.txt new file mode 100644 index 0000000000000..9ae57f103b432 --- /dev/null +++ b/stdlib/public/Backtracing/CMakeLists.txt @@ -0,0 +1,53 @@ +#===--- CMakeLists.txt - Backtracing support library -----------------------===# +# +# This source file is part of the Swift.org open source project +# +# Copyright (c) 2023 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 +# +#===------------------------------------------------------------------------===# + +set(swift_backtracing_link_libraries + swiftCore +) + +set(BACKTRACING_SOURCES + Backtrace.swift + BacktraceFormatter.swift + Context.swift + CoreSymbolication.swift + FramePointerUnwinder.swift + MemoryReader.swift + Registers.swift + SymbolicatedBacktrace.swift + Utils.swift + + get-cpu-context.S +) + +add_swift_target_library(swift_Backtracing ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_STDLIB + ${BACKTRACING_SOURCES} + + SWIFT_MODULE_DEPENDS_IOS Darwin _Concurrency + SWIFT_MODULE_DEPENDS_OSX Darwin _Concurrency + SWIFT_MODULE_DEPENDS_TVOS Darwin _Concurrency + SWIFT_MODULE_DEPENDS_WATCHOS Darwin _Concurrency + SWIFT_MODULE_DEPENDS_MACCATALYST Darwin _Concurrency + SWIFT_MODULE_DEPENDS_LINUX Glibc _Concurrency + SWIFT_MODULE_DEPENDS_WINDOWS CRT _Concurrency + + LINK_LIBRARIES ${swift_backtracing_link_libraries} + + SWIFT_COMPILE_FLAGS + ${SWIFT_STANDARD_LIBRARY_SWIFT_FLAGS} + -parse-stdlib + + LINK_FLAGS + ${SWIFT_RUNTIME_SWIFT_LINK_FLAGS} + + INSTALL_IN_COMPONENT stdlib + MACCATALYST_BUILD_FLAVOR "zippered" +) diff --git a/stdlib/public/Backtracing/Context.swift b/stdlib/public/Backtracing/Context.swift new file mode 100644 index 0000000000000..5ecb8e807d2cb --- /dev/null +++ b/stdlib/public/Backtracing/Context.swift @@ -0,0 +1,865 @@ +//===--- Context.swift - Unwind context structure -------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 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 +// +//===----------------------------------------------------------------------===// +// +// Defines the Context protocol and some concrete implementations for various +// different types of CPU. +// +// Context holds register values during unwinding. +// +//===----------------------------------------------------------------------===// + +import Swift + +@_implementationOnly import _SwiftBacktracingShims + +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) +@_implementationOnly import Darwin.Mach +#endif + +@_spi(Contexts) public enum ContextError: Error { + case unableToFormTLSAddress +} + +@_spi(Contexts) public protocol Context: CustomStringConvertible { + /// Represents a machine address for this type of machine + associatedtype Address: FixedWidthInteger + + /// Represents a size for this type of machine + associatedtype Size: FixedWidthInteger + + /// The type of a general purpose register on this machine + associatedtype GPRValue: FixedWidthInteger + + /// An enumerated type defining the registers for the machine (this comes + /// from the architecture specific DWARF specification). + associatedtype Register: RawRepresentable where Register.RawValue == Int + + /// The program counter; this is likely a return address + var programCounter: GPRValue { get set } + + /// The stack pointer + var stackPointer: GPRValue { get set } + + /// The frame pointer + var framePointer: GPRValue { get set } + + /// The CFA as defined by the relevant architecture specific DWARF + /// specification. For the architectures we have currently, it turns out + /// that this is the stack pointer, but it might in general be some other + /// thing. + var callFrameAddress: GPRValue { get set } + + /// The number of register slots to reserve in the unwinder (this corresponds + /// to the DWARF register numbers, which is why some of these reserve a lot + /// of slots). + static var registerCount: Int { get } + + /// Given a thread local address, form a genuine machine address + func formTLSAddress(threadLocal: Address) throws -> Address + + /// Get the value of the specified general purpose register, or nil if unknown + func getRegister(_ register: Register) -> GPRValue? + + /// Set the value of the specified general purpose register (or mark it as + /// unknown if nil is passed) + mutating func setRegister(_ register: Register, to value: GPRValue?) + + /// Set all of the registers in bulk + mutating func setRegisters(_ registers: [GPRValue?]) + + /// Strip any pointer authentication that might apply from an address. + static func stripPtrAuth(address: Address) -> Address + + /// Test if an address is appropriately aligned for the stack. + static func isAlignedForStack(framePointer: Address) -> Bool +} + +extension Context { + public func formTLSAddress(threadLocal: Address) throws -> Address { + throw ContextError.unableToFormTLSAddress + } + + public mutating func setRegisters(_ registers: [GPRValue?]) { + for (ndx, value) in registers.enumerated() { + if let reg = Register(rawValue: ndx) { + setRegister(reg, to: value) + } + } + } + + public static func stripPtrAuth(address: Address) -> Address { + return address + } +} + +// .. Extensions to the GPR structures ......................................... + +// We need these because the arrays in the _gprs structs (which are defined +// in C so that the layout is fixed) get imported as tuples. + +extension x86_64_gprs { + func getR(_ ndx: Int) -> UInt64 { + return withUnsafePointer(to: _r) { + $0.withMemoryRebound(to: UInt64.self, capacity: 16) { + $0[ndx] + } + } + } + + mutating func setR(_ ndx: Int, to value: UInt64) { + withUnsafeMutablePointer(to: &_r) { + $0.withMemoryRebound(to: UInt64.self, capacity: 16) { + $0[ndx] = value + } + } + valid |= 1 << ndx + } +} + +extension i386_gprs { + func getR(_ ndx: Int) -> UInt32 { + return withUnsafePointer(to: _r) { + $0.withMemoryRebound(to: UInt32.self, capacity: 8) { + $0[ndx] + } + } + } + + mutating func setR(_ ndx: Int, to value: UInt32) { + withUnsafeMutablePointer(to: &_r) { + $0.withMemoryRebound(to: UInt32.self, capacity: 8) { + $0[ndx] = value + } + } + valid |= 1 << ndx + } +} + +extension arm64_gprs { + func getX(_ ndx: Int) -> UInt64 { + return withUnsafePointer(to: _x) { + $0.withMemoryRebound(to: UInt64.self, capacity: 32) { + $0[ndx] + } + } + } + + mutating func setX(_ ndx: Int, to value: UInt64) { + withUnsafeMutablePointer(to: &_x) { + $0.withMemoryRebound(to: UInt64.self, capacity: 32) { + $0[ndx] = value + } + } + valid |= 1 << ndx + } +} + +extension arm_gprs { + func getR(_ ndx: Int) -> UInt32 { + return withUnsafePointer(to: _r) { + $0.withMemoryRebound(to: UInt32.self, capacity: 16) { + $0[ndx] + } + } + } + + mutating func setR(_ ndx: Int, to value: UInt32) { + withUnsafeMutablePointer(to: &_r) { + $0.withMemoryRebound(to: UInt32.self, capacity: 16) { + $0[ndx] = value + } + } + valid |= 1 << ndx + } +} + +// .. x86-64 ................................................................... + +@_spi(Contexts) public struct X86_64Context: Context { + public typealias Address = UInt64 + public typealias Size = UInt64 + public typealias GPRValue = UInt64 + public typealias Register = X86_64Register + + var gprs = x86_64_gprs() + + public var programCounter: Address { + get { return gprs.rip } + set { + gprs.rip = newValue + gprs.valid |= 1 << 20 + } + } + public var framePointer: Address { + get { return gprs.getR(X86_64Register.rbp.rawValue) } + set { + gprs.setR(X86_64Register.rbp.rawValue, to: newValue) + } + } + public var stackPointer: Address { + get { return gprs.getR(X86_64Register.rsp.rawValue) } + set { + gprs.setR(X86_64Register.rsp.rawValue, to: newValue) + } + } + + public var callFrameAddress: GPRValue { + get { return stackPointer } + set { stackPointer = newValue } + } + + public static var registerCount: Int { return 56 } + + #if (os(macOS) || os(iOS) || os(watchOS) || os(tvOS)) && arch(x86_64) + init?(from thread: thread_t) { + var state = darwin_x86_64_thread_state() + let kr = mach_thread_get_state(thread, x86_THREAD_STATE64, &state) + if kr != KERN_SUCCESS { + return nil + } + + self.init(from: state) + } + + init(with mctx: darwin_x86_64_mcontext) { + self.init(from: mctx.ss) + } + + init(from state: darwin_x86_64_thread_state) { + gprs.setR(X86_64Register.rax.rawValue, to: state.rax) + gprs.setR(X86_64Register.rbx.rawValue, to: state.rbx) + gprs.setR(X86_64Register.rcx.rawValue, to: state.rcx) + gprs.setR(X86_64Register.rdx.rawValue, to: state.rdx) + gprs.setR(X86_64Register.rdi.rawValue, to: state.rdi) + gprs.setR(X86_64Register.rsi.rawValue, to: state.rsi) + gprs.setR(X86_64Register.rbp.rawValue, to: state.rbp) + gprs.setR(X86_64Register.rsp.rawValue, to: state.rsp) + gprs.setR(X86_64Register.r8.rawValue, to: state.r8) + gprs.setR(X86_64Register.r9.rawValue, to: state.r9) + gprs.setR(X86_64Register.r10.rawValue, to: state.r10) + gprs.setR(X86_64Register.r11.rawValue, to: state.r11) + gprs.setR(X86_64Register.r12.rawValue, to: state.r12) + gprs.setR(X86_64Register.r13.rawValue, to: state.r13) + gprs.setR(X86_64Register.r14.rawValue, to: state.r14) + gprs.setR(X86_64Register.r15.rawValue, to: state.r15) + gprs.rip = state.rip + gprs.rflags = state.rflags + gprs.cs = UInt16(state.cs) + gprs.fs = UInt16(state.fs) + gprs.gs = UInt16(state.gs) + gprs.valid = 0x1fffff + } + + public static func fromHostThread(_ thread: Any) -> HostContext? { + return X86_64Context(from: thread as! thread_t) + } + + public static func fromHostMContext(_ mcontext: Any) -> HostContext { + return X86_64Context(with: mcontext as! darwin_x86_64_mcontext) + } + #endif + + #if arch(x86_64) + @_silgen_name("_swift_get_cpu_context") + static func _swift_get_cpu_context() -> X86_64Context + + public static func withCurrentContext(fn: (X86_64Context) throws -> T) rethrows -> T { + return try fn(_swift_get_cpu_context()) + } + #endif + + private func validNdx(_ register: Register) -> Int? { + switch register { + case .rax ... .r15: + return register.rawValue + case .rflags: + return 16 + case .cs: + return 17 + case .fs: + return 18 + case .gs: + return 19 + default: + return nil + } + } + + private func isValid(_ register: Register) -> Bool { + guard let ndx = validNdx(register) else { + return false + } + return (gprs.valid & (UInt64(1) << ndx)) != 0 + } + + private mutating func setValid(_ register: Register) { + guard let ndx = validNdx(register) else { + return + } + gprs.valid |= UInt64(1) << ndx + } + + private mutating func clearValid(_ register: Register) { + guard let ndx = validNdx(register) else { + return + } + gprs.valid &= ~(UInt64(1) << ndx) + } + + public func getRegister(_ register: Register) -> GPRValue? { + if !isValid(register) { + return nil + } + + switch register { + case .rax ... .r15: + return gprs.getR(register.rawValue) + case .rflags: return gprs.rflags + case .cs: return UInt64(gprs.cs) + case .fs: return UInt64(gprs.fs) + case .gs: return UInt64(gprs.gs) + default: + return nil + } + } + + public mutating func setRegister(_ register: Register, to value: GPRValue?) { + if let value = value { + switch register { + case .rax ... .r15: + gprs.setR(register.rawValue, to: value) + case .rflags: + gprs.rflags = value + setValid(register) + case .cs: + gprs.cs = UInt16(value) + setValid(register) + case .fs: + gprs.fs = UInt16(value) + setValid(register) + case .gs: + gprs.gs = UInt16(value) + setValid(register) + default: + return + } + } else { + clearValid(register) + } + } + + public var description: String { + return """ + rax: \(hex(gprs.getR(0))) rbx: \(hex(gprs.getR(3))) rcx: \(hex(gprs.getR(2))) + rdx: \(hex(gprs.getR(1))) rsi: \(hex(gprs.getR(4))) rdi: \(hex(gprs.getR(5))) + rbp: \(hex(gprs.getR(6))) rsp: \(hex(gprs.getR(7))) r8: \(hex(gprs.getR(8))) + r9: \(hex(gprs.getR(9))) r10: \(hex(gprs.getR(10))) r11: \(hex(gprs.getR(11))) + r12: \(hex(gprs.getR(12))) r13: \(hex(gprs.getR(13))) r14: \(hex(gprs.getR(14))) + r15: \(hex(gprs.getR(15))) + + cs: \(hex(gprs.cs)) fs: \(hex(gprs.fs)) gs: \(hex(gprs.gs)) + + rip: \(hex(gprs.rip)) rflags: \(hex(gprs.rflags)) + """ + } + + public static func isAlignedForStack(framePointer: Address) -> Bool { + return (framePointer & 0xf) == 0 + } + + #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + internal static var coreSymbolicationArchitecture: CSArchitecture { + return kCSArchitectureX86_64 + } + #endif +} + +// .. i386 ..................................................................... + +@_spi(Contexts) public struct I386Context: Context { + public typealias Address = UInt32 + public typealias Size = UInt32 + public typealias GPRValue = UInt32 + public typealias Register = I386Register + + var gprs = i386_gprs() + + public var programCounter: GPRValue { + get { return gprs.eip } + set { + gprs.eip = newValue + gprs.valid |= 1 << 15 + } + } + + public var framePointer: GPRValue { + get { return gprs.getR(I386Register.ebp.rawValue) } + set { gprs.setR(I386Register.ebp.rawValue, to: newValue) } + } + + public var stackPointer: GPRValue { + get { return gprs.getR(I386Register.esp.rawValue) } + set { gprs.setR(I386Register.esp.rawValue, to: newValue) } + } + + public var callFrameAddress: GPRValue { + get { return stackPointer } + set { stackPointer = newValue } + } + + public static var registerCount: Int { return 50 } + + #if arch(i386) + @_silgen_name("_swift_get_cpu_context") + static func _swift_get_cpu_context() -> I386Context + + public static func withCurrentContext(fn: (I386Context) throws -> T) rethrows -> T { + return try fn(_swift_get_cpu_context()) + } + #endif + + private func validNdx(_ register: Register) -> Int? { + switch register { + case .eax ... .edi: + return register.rawValue + case .eflags: + return 8 + case .es, .cs, .ss, .ds, .fs, .gs: + return 9 + register.rawValue - Register.es.rawValue + case .ra: + return 15 + default: + return nil + } + } + + private func isValid(_ register: Register) -> Bool { + guard let ndx = validNdx(register) else { + return false + } + return (gprs.valid & (UInt32(1) << ndx)) != 0 + } + + private mutating func setValid(_ register: Register) { + guard let ndx = validNdx(register) else { + return + } + gprs.valid |= UInt32(1) << ndx + } + + private mutating func clearValid(_ register: Register) { + guard let ndx = validNdx(register) else { + return + } + gprs.valid &= ~(UInt32(1) << ndx) + } + + public func getRegister(_ register: Register) -> GPRValue? { + if !isValid(register) { + return nil + } + switch register { + case .eax ... .edi: + return gprs.getR(register.rawValue) + case .eflags: return gprs.eflags + case .es ... .gs: + return withUnsafeBytes(of: gprs.segreg) { ptr in + return ptr.withMemoryRebound(to: GPRValue.self) { regs in + return regs[register.rawValue - Register.es.rawValue] + } + } + case .ra: return gprs.eip + default: + return nil + } + } + + public mutating func setRegister(_ register: Register, to value: GPRValue?) { + if let value = value { + switch register { + case .eax ... .edi: + gprs.setR(register.rawValue, to: value) + case .eflags: + gprs.eflags = value + setValid(register) + case .es ... .gs: + withUnsafeMutableBytes(of: &gprs.segreg) { ptr in + ptr.withMemoryRebound(to: GPRValue.self) { regs in + regs[register.rawValue - Register.es.rawValue] = value + } + } + setValid(register) + case .ra: + gprs.eip = value + setValid(register) + default: + return + } + } else { + clearValid(register) + } + } + + public var description: String { + return """ + eax: \(hex(gprs.getR(0))) ebx: \(hex(gprs.getR(3))) ecx: \(hex(gprs.getR(1))) edx: \(hex(gprs.getR(2))) + esi: \(hex(gprs.getR(6))) edi: \(hex(gprs.getR(7))) ebp: \(hex(gprs.getR(5))) esp: \(hex(gprs.getR(4))) + + es: \(hex(gprs.segreg.0)) cs: \(hex(gprs.segreg.1)) ss: \(hex(gprs.segreg.2)) ds: \(hex(gprs.segreg.3)) fs: \(hex(gprs.segreg.4)) gs: \(hex(gprs.segreg.5)) + + eip: \(hex(gprs.eip)) eflags: \(hex(gprs.eflags)) + """ + } + + public static func isAlignedForStack(framePointer: Address) -> Bool { + return (framePointer & 0xf) == 8 + } + + #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + internal static var coreSymbolicationArchitecture: CSArchitecture { + return kCSArchitectureI386 + } + #endif +} + +// .. ARM64 .................................................................... + +@_spi(Contexts) public struct ARM64Context: Context { + public typealias Address = UInt64 + public typealias Size = UInt64 + public typealias GPRValue = UInt64 + public typealias Register = ARM64Register + + var gprs = arm64_gprs() + + public var programCounter: GPRValue { + get { return gprs.pc } + set { + gprs.pc = newValue + gprs.valid |= 1 << 32 + } + } + + public var stackPointer: GPRValue { + get { return gprs.getX(ARM64Register.sp.rawValue) } + set { + gprs.setX(ARM64Register.sp.rawValue, to: newValue) + } + } + + public var framePointer: GPRValue { + get { return gprs.getX(ARM64Register.x29.rawValue) } + set { + gprs.setX(ARM64Register.x29.rawValue, to: newValue) + } + } + + public var callFrameAddress: GPRValue { + get { return stackPointer } + set { stackPointer = newValue } + } + + public static var registerCount: Int { return 40 } + + #if (os(macOS) || os(iOS) || os(watchOS) || os(tvOS)) && arch(arm64) + init?(from thread: thread_t) { + var state = darwin_arm64_thread_state() + let kr = mach_thread_get_state(thread, ARM_THREAD_STATE64, &state) + if kr != KERN_SUCCESS { + return nil + } + + self.init(from: state) + } + + init(with mctx: darwin_arm64_mcontext) { + self.init(from: mctx.ss) + } + + init(from state: darwin_arm64_thread_state) { + withUnsafeMutablePointer(to: &gprs._x) { + $0.withMemoryRebound(to: UInt64.self, capacity: 32){ to in + withUnsafePointer(to: state._x) { + $0.withMemoryRebound(to: UInt64.self, capacity: 29){ from in + for n in 0..<29 { + to[n] = from[n] + } + } + } + + to[29] = state.fp + to[30] = state.lr + to[31] = state.sp + } + } + gprs.pc = state.pc + gprs.valid = 0x1ffffffff + } + + public static func fromHostThread(_ thread: Any) -> HostContext? { + return ARM64Context(from: thread as! thread_t) + } + + public static func fromHostMContext(_ mcontext: Any) -> HostContext { + return ARM64Context(with: mcontext as! darwin_arm64_mcontext) + } +#endif + + #if arch(arm64) + @_silgen_name("_swift_get_cpu_context") + static func _swift_get_cpu_context() -> ARM64Context + + public static func withCurrentContext(fn: (ARM64Context) throws -> T) rethrows -> T { + return try fn(_swift_get_cpu_context()) + } + #endif + + private func isValid(_ register: Register) -> Bool { + if register.rawValue < 33 { + return (gprs.valid & (UInt64(1) << register.rawValue)) != 0 + } + return false + } + + private mutating func setValid(_ register: Register) { + if register.rawValue < 33 { + gprs.valid |= UInt64(1) << register.rawValue + } + } + + private mutating func clearValid(_ register: Register) { + if register.rawValue < 33 { + gprs.valid &= ~(UInt64(1) << register.rawValue) + } + } + + public func getRegister(_ reg: Register) -> GPRValue? { + if !isValid(reg) { + return nil + } + switch reg { + case .x0 ... .sp: + return gprs.getX(reg.rawValue) + case .pc: + return gprs.pc + default: + return nil + } + } + + public mutating func setRegister(_ reg: Register, to value: GPRValue?) { + if let value = value { + switch reg { + case .x0 ... .sp: + gprs.setX(reg.rawValue, to: value) + case .pc: + gprs.pc = value + setValid(reg) + default: + break + } + } else { + clearValid(reg) + } + } + + public var description: String { + return """ + x0: \(hex(gprs.getX(0))) x1: \(hex(gprs.getX(1))) + x2: \(hex(gprs.getX(2))) x3: \(hex(gprs.getX(3))) + x4: \(hex(gprs.getX(4))) x5: \(hex(gprs.getX(5))) + x6: \(hex(gprs.getX(6))) x7: \(hex(gprs.getX(7))) + x8: \(hex(gprs.getX(8))) x9: \(hex(gprs.getX(9))) + x10: \(hex(gprs.getX(10))) x11: \(hex(gprs.getX(11))) + x12: \(hex(gprs.getX(12))) x13: \(hex(gprs.getX(13))) + x14: \(hex(gprs.getX(14))) x15: \(hex(gprs.getX(15))) + x16: \(hex(gprs.getX(16))) x17: \(hex(gprs.getX(17))) + x18: \(hex(gprs.getX(18))) x19: \(hex(gprs.getX(19))) + x20: \(hex(gprs.getX(20))) x21: \(hex(gprs.getX(21))) + x22: \(hex(gprs.getX(22))) x23: \(hex(gprs.getX(23))) + x24: \(hex(gprs.getX(24))) x25: \(hex(gprs.getX(25))) + x26: \(hex(gprs.getX(26))) x27: \(hex(gprs.getX(27))) + x28: \(hex(gprs.getX(28))) + + fp: \(hex(gprs.getX(29))) (aka x29) + lr: \(hex(gprs.getX(30))) (aka x30) + sp: \(hex(gprs.getX(31))) (aka x31) + + pc: \(hex(gprs.pc)) + """ + } + + public static func isAlignedForStack(framePointer: Address) -> Bool { + return (framePointer & 1) == 0 + } + + #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + public static func stripPtrAuth(address: Address) -> Address { + // Is there a better way to do this? It'd be easy if we just wanted to + // strip for the *host*, but we might conceivably want this under other + // circumstances too. + return address & 0x00007fffffffffff + } + + internal static var coreSymbolicationArchitecture: CSArchitecture { + return kCSArchitectureArm64 + } + #endif +} + +// .. 32-bit ARM ............................................................... + +@_spi(Contexts) public struct ARMContext: Context { + public typealias Address = UInt32 + public typealias Size = UInt32 + public typealias GPRValue = UInt32 + public typealias Register = ARMRegister + + var gprs = arm_gprs() + + public var programCounter: GPRValue { + get { return gprs.getR(ARMRegister.r15.rawValue) } + set { gprs.setR(ARMRegister.r15.rawValue, to: newValue) } + } + + public var stackPointer: GPRValue { + get { return gprs.getR(ARMRegister.r13.rawValue) } + set { gprs.setR(ARMRegister.r13.rawValue, to: newValue) } + } + + public var framePointer: GPRValue { + get { return gprs.getR(ARMRegister.r11.rawValue) } + set { gprs.setR(ARMRegister.r11.rawValue, to: newValue) } + } + + public var callFrameAddress: GPRValue { + get { return stackPointer } + set { stackPointer = newValue } + } + + public static var registerCount: Int { return 16 } + +#if arch(arm) + @_silgen_name("_swift_get_cpu_context") + static func _swift_get_cpu_context() -> ARMContext + + public static func withCurrentContext(fn: (ARMContext) throws -> T) rethrows -> T { + return try fn(_swift_get_cpu_context()) + } +#endif + + private func isValid(_ register: Register) -> Bool { + if register.rawValue < 16 { + return (gprs.valid & (UInt32(1) << register.rawValue)) != 0 + } + return false + } + + private mutating func setValid(_ register: Register) { + if register.rawValue < 16 { + gprs.valid |= UInt32(1) << register.rawValue + } + } + + private mutating func clearValid(_ register: Register) { + if register.rawValue < 16 { + gprs.valid &= ~(UInt32(1) << register.rawValue) + } + } + + public func getRegister(_ reg: Register) -> GPRValue? { + if !isValid(reg) { + return nil + } + switch reg { + case .r0 ... .r15: + return gprs.getR(reg.rawValue) + default: + return nil + } + } + + public mutating func setRegister(_ reg: Register, to value: GPRValue?) { + if let value = value { + switch reg { + case .r0 ... .r15: + gprs.setR(reg.rawValue, to: value) + default: + break + } + } else { + clearValid(reg) + } + } + + public var description: String { + return """ + r0: \(hex(gprs.getR(0))) r1: \(hex(gprs.getR(1))) + r2: \(hex(gprs.getR(2))) r3: \(hex(gprs.getR(3))) + r4: \(hex(gprs.getR(4))) r5: \(hex(gprs.getR(5))) + r6: \(hex(gprs.getR(6))) r7: \(hex(gprs.getR(7))) + r8: \(hex(gprs.getR(8))) r9: \(hex(gprs.getR(9))) + r10: \(hex(gprs.getR(10))) + + fp: \(hex(gprs.getR(11))) (aka r11) + ip: \(hex(gprs.getR(12))) (aka r12) + sp: \(hex(gprs.getR(13))) (aka r13) + lr: \(hex(gprs.getR(14))) (aka r14) + pc: \(hex(gprs.getR(15))) (aka r15) + """ + } + + public static func isAlignedForStack(framePointer: Address) -> Bool { + return (framePointer & 1) == 0 + } + + #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + internal static var coreSymbolicationArchitecture: CSArchitecture { + return kCSArchitectureArmV7K + } + #endif +} + +// .. Darwin specifics ......................................................... + +#if os(macOS) +private func mach_thread_get_state(_ thread: thread_t, + _ flavor: CInt, + _ result: inout T) -> kern_return_t { + var count: mach_msg_type_number_t + = mach_msg_type_number_t(MemoryLayout.stride + / MemoryLayout.stride) + + return withUnsafeMutablePointer(to: &result) { ptr in + ptr.withMemoryRebound(to: natural_t.self, capacity: Int(count)) { intPtr in + return thread_get_state(thread, + thread_state_flavor_t(flavor), + intPtr, + &count) + } + } +} +#endif + +// .. HostContext .............................................................. + +/// HostContext is an alias for the appropriate context for the machine on which +/// the code was compiled. +#if arch(x86_64) +@_spi(Contexts) public typealias HostContext = X86_64Context +#elseif arch(i386) +@_spi(Contexts) public typealias HostContext = I386Context +#elseif arch(arm64) +@_spi(Contexts) public typealias HostContext = ARM64Context +#elseif arch(arm) +@_spi(Contexts) public typealias HostContext = ARMContext +#endif diff --git a/stdlib/public/Backtracing/CoreSymbolication.swift b/stdlib/public/Backtracing/CoreSymbolication.swift new file mode 100644 index 0000000000000..9ae631cc69681 --- /dev/null +++ b/stdlib/public/Backtracing/CoreSymbolication.swift @@ -0,0 +1,360 @@ +//===--- CoreSymbolication.swift - Shims for CoreSymbolication ------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 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 +// +//===----------------------------------------------------------------------===// +// +// CoreSymbolication is a private framework, which makes it tricky to link +// with from here and also means there are no headers on customer builds. +// +//===----------------------------------------------------------------------===// + +#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS) + +import Swift + +@_implementationOnly import Darwin +@_implementationOnly import CoreFoundation + +@_implementationOnly import _SwiftBacktracingShims + +// .. Dynamic binding .......................................................... + +private let coreSymbolicationPath = + "/System/Library/PrivateFrameworks/CoreSymbolication.framework/CoreSymbolication" +private let coreSymbolicationHandle = dlopen(coreSymbolicationPath, RTLD_LAZY)! + +private let crashReporterSupportPath = + "/System/Library/PrivateFrameworks/CrashReporterSupport.framework/CrashReporterSupport" + +private let crashReporterSupportHandle = dlopen(crashReporterSupportPath, RTLD_LAZY)! + +private func symbol(_ handle: UnsafeMutableRawPointer, _ name: String) -> T { + guard let result = dlsym(handle, name) else { + fatalError("Unable to look up \(name) in CoreSymbolication") + } + return unsafeBitCast(result, to: T.self) +} + +private enum Sym { + // CRCopySanitizedPath + static let CRCopySanitizedPath: @convention(c) (CFString, CFIndex) -> CFString = + symbol(crashReporterSupportHandle, "CRCopySanitizedPath") + + // Base functionality + static let CSRetain: @convention(c) (CSTypeRef) -> CSTypeRef = + symbol(coreSymbolicationHandle, "CSRetain") + static let CSRelease: @convention(c) (CSTypeRef) -> () = + symbol(coreSymbolicationHandle, "CSRelease") + static let CSEqual: @convention(c) (CSTypeRef, CSTypeRef) -> CBool = + symbol(coreSymbolicationHandle, "CSEqual") + static let CSIsNull: @convention(c) (CSTypeRef) -> CBool = + symbol(coreSymbolicationHandle, "CSIsNull") + + // CSSymbolicator + static let CSSymbolicatorCreateWithBinaryImageList: + @convention(c) (UnsafeMutablePointer, + UInt32, UInt32, CSNotificationBlock?) -> CSSymbolicatorRef = + symbol(coreSymbolicationHandle, "CSSymbolicatorCreateWithBinaryImageList") + + static let CSSymbolicatorGetSymbolOwnerWithAddressAtTime: + @convention(c) (CSSymbolicatorRef, mach_vm_address_t, + CSMachineTime) -> CSSymbolOwnerRef = + symbol(coreSymbolicationHandle, "CSSymbolicatorGetSymbolOwnerWithAddressAtTime") + static let CSSymbolicatorForeachSymbolOwnerAtTime: + @convention(c) (CSSymbolicatorRef, CSMachineTime, @convention(block) (CSSymbolOwnerRef) -> Void) -> UInt = + symbol(coreSymbolicationHandle, "CSSymbolicatorForeachSymbolOwnerAtTime") + + // CSSymbolOwner + static let CSSymbolOwnerGetName: + @convention(c) (CSSymbolOwnerRef) -> UnsafePointer? = + symbol(coreSymbolicationHandle, "CSSymbolOwnerGetName") + static let CSSymbolOwnerGetSymbolWithAddress: + @convention(c) (CSSymbolOwnerRef, mach_vm_address_t) -> CSSymbolRef = + symbol(coreSymbolicationHandle, "CSSymbolOwnerGetSymbolWithAddress") + static let CSSymbolOwnerGetSourceInfoWithAddress: + @convention(c) (CSSymbolOwnerRef, mach_vm_address_t) -> CSSourceInfoRef = + symbol(coreSymbolicationHandle, "CSSymbolOwnerGetSourceInfoWithAddress") + static let CSSymbolOwnerForEachStackFrameAtAddress: + @convention(c) (CSSymbolOwnerRef, mach_vm_address_t, CSStackFrameIterator) -> UInt = + symbol(coreSymbolicationHandle, "CSSymbolOwnerForEachStackFrameAtAddress") + static let CSSymbolOwnerGetBaseAddress: + @convention(c) (CSSymbolOwnerRef) -> mach_vm_address_t = + symbol(coreSymbolicationHandle, "CSSymbolOwnerGetBaseAddress") + + // CSSymbol + static let CSSymbolGetRange: + @convention(c) (CSSymbolRef) -> CSRange = + symbol(coreSymbolicationHandle, "CSSymbolGetRange") + static let CSSymbolGetName: + @convention(c) (CSSymbolRef) -> UnsafePointer? = + symbol(coreSymbolicationHandle, "CSSymbolGetName") + static let CSSymbolGetMangledName: + @convention(c) (CSSymbolRef) -> UnsafePointer? = + symbol(coreSymbolicationHandle, "CSSymbolGetMangledName") + + // CSSourceInfo + static let CSSourceInfoGetPath: + @convention(c) (CSSourceInfoRef) -> UnsafePointer? = + symbol(coreSymbolicationHandle, "CSSourceInfoGetPath") + static let CSSourceInfoGetLineNumber: + @convention(c) (CSSourceInfoRef) -> UInt32 = + symbol(coreSymbolicationHandle, "CSSourceInfoGetLineNumber") + static let CSSourceInfoGetColumn: + @convention(c) (CSSourceInfoRef) -> UInt32 = + symbol(coreSymbolicationHandle, "CSSourceInfoGetColumn") +} + +// .. Crash Reporter support ................................................... + +// We can't import swiftFoundation here, so there's no automatic bridging for +// CFString. As a result, we need to do the dance manually. + +private func toCFString(_ s: String) -> CFString! { + let bytes = Array(s.utf8) + return bytes.withUnsafeBufferPointer{ + return CFStringCreateWithBytes(nil, + $0.baseAddress, + $0.count, + CFStringBuiltInEncodings.UTF8.rawValue, + false) + } +} + +private func fromCFString(_ cf: CFString) -> String { + let length = CFStringGetLength(cf) + if length == 0 { + return "" + } + + if let ptr = CFStringGetCStringPtr(cf, + CFStringBuiltInEncodings.ASCII.rawValue) { + return ptr.withMemoryRebound(to: UInt8.self, capacity: length) { + return String(decoding: UnsafeBufferPointer(start: $0, count: length), + as: UTF8.self) + } + } else { + var byteLen = CFIndex(0) + + CFStringGetBytes(cf, + CFRangeMake(0, length), + CFStringBuiltInEncodings.UTF8.rawValue, + 0, + false, + nil, + 0, + &byteLen) + + let buffer = UnsafeMutableBufferPointer.allocate(capacity: byteLen) + defer { + buffer.deallocate() + } + + CFStringGetBytes(cf, CFRangeMake(0, length), + CFStringBuiltInEncodings.UTF8.rawValue, + 0, false, buffer.baseAddress, buffer.count, nil) + + return String(decoding: buffer, as: UTF8.self) + } +} + +func CRCopySanitizedPath(_ path: String, _ options: Int) -> String { + return fromCFString(Sym.CRCopySanitizedPath(toCFString(path), CFIndex(options))) +} + +// .. Base functionality ....................................................... + +func CSRetain(_ obj: CSTypeRef) -> CSTypeRef { + return Sym.CSRetain(obj) +} + +func CSRelease(_ obj: CSTypeRef) { + Sym.CSRelease(obj) +} + +func CSEqual(_ a: CSTypeRef, _ b: CSTypeRef) -> Bool { + return Sym.CSEqual(a, b) +} + +func CSIsNull(_ obj: CSTypeRef) -> Bool { + return Sym.CSIsNull(obj) +} + +// .. CSSymbolicator ........................................................... + + +struct BinaryRelocationInformation { + var base: mach_vm_address_t + var extent: mach_vm_address_t + var name: String +} + +struct BinaryImageInformation { + var base: mach_vm_address_t + var extent: mach_vm_address_t + var uuid: CFUUIDBytes + var arch: CSArchitecture + var path: String + var relocations: [BinaryRelocationInformation] + var flags: UInt32 +} + +func CSSymbolicatorCreateWithBinaryImageList( + _ imageInfo: [BinaryImageInformation], + _ flags: UInt32, + _ notificationBlock: CSNotificationBlock?) -> CSSymbolicatorRef { + + // Convert the Swifty types above to suitable input for the C API + var pathBuf: [UInt8] = [] + let imageList = UnsafeMutableBufferPointer.allocate(capacity: imageInfo.count) + defer { + imageList.deallocate() + } + + var totalRelocations = 0 + for image in imageInfo { + totalRelocations += image.relocations.count + + pathBuf.insert(contentsOf: image.path.utf8, at: pathBuf.count) + pathBuf.append(0) + } + + let relocationList = UnsafeMutableBufferPointer.allocate(capacity: totalRelocations) + defer { + relocationList.deallocate() + } + + return pathBuf.withUnsafeBufferPointer { + $0.withMemoryRebound(to: CChar.self) { pathData in + var pathPtr = pathData.baseAddress! + var relocationPtr = relocationList.baseAddress! + + for (n, image) in imageInfo.enumerated() { + imageList[n].base = image.base + imageList[n].extent = image.extent + imageList[n].uuid = image.uuid + imageList[n].arch = image.arch + imageList[n].path = pathPtr + imageList[n].relocations = relocationPtr + imageList[n].relocationCount = UInt32(image.relocations.count) + imageList[n].flags = image.flags + + pathPtr += strlen(pathPtr) + 1 + + for relocation in image.relocations { + relocationPtr.pointee.base = relocation.base + relocationPtr.pointee.extent = relocation.extent + withUnsafeMutablePointer(to: &relocationPtr.pointee.name) { + $0.withMemoryRebound(to: CChar.self, capacity: 17) { buf in + var utf8Iterator = relocation.name.utf8.makeIterator() + var ndx = 0 + while let ch = utf8Iterator.next(), ndx < 16 { + buf[ndx] = CChar(bitPattern: ch) + ndx += 1 + } + buf[ndx] = 0 + } + } + + relocationPtr += 1 + } + } + + return Sym.CSSymbolicatorCreateWithBinaryImageList( + imageList.baseAddress!, + UInt32(imageList.count), + flags, + notificationBlock + ) + } + } +} + +func CSSymbolicatorGetSymbolOwnerWithAddressAtTime( + _ symbolicator: CSSymbolicatorRef, + _ addr: mach_vm_address_t, + _ time: CSMachineTime +) -> CSSymbolOwnerRef { + return Sym.CSSymbolicatorGetSymbolOwnerWithAddressAtTime(symbolicator, + addr, time) +} + +func CSSymbolicatorForeachSymbolOwnerAtTime( + _ symbolicator: CSSymbolicatorRef, + _ time: CSMachineTime, + _ symbolIterator: (CSSymbolOwnerRef) -> Void + ) -> UInt { + return Sym.CSSymbolicatorForeachSymbolOwnerAtTime(symbolicator, time, + symbolIterator) +} + +// .. CSSymbolOwner ............................................................ + +func CSSymbolOwnerGetName(_ sym: CSTypeRef) -> String? { + Sym.CSSymbolOwnerGetName(sym) + .map(String.init(cString:)) +} + +func CSSymbolOwnerGetSymbolWithAddress( + _ owner: CSSymbolOwnerRef, + _ address: mach_vm_address_t +) -> CSSymbolRef { + return Sym.CSSymbolOwnerGetSymbolWithAddress(owner, address) +} + +func CSSymbolOwnerGetSourceInfoWithAddress( + _ owner: CSSymbolOwnerRef, + _ address: mach_vm_address_t +) -> CSSourceInfoRef { + return Sym.CSSymbolOwnerGetSourceInfoWithAddress(owner, address) +} + +func CSSymbolOwnerForEachStackFrameAtAddress( + _ owner: CSSymbolOwnerRef, + _ address: mach_vm_address_t, + _ iterator: CSStackFrameIterator +) -> UInt { + return Sym.CSSymbolOwnerForEachStackFrameAtAddress(owner, address, iterator) +} + +func CSSymbolOwnerGetBaseAddress( + _ owner: CSSymbolOwnerRef +) -> mach_vm_address_t { + return Sym.CSSymbolOwnerGetBaseAddress(owner) +} + +// .. CSSymbol ................................................................. + +func CSSymbolGetRange(_ symbol: CSSymbolRef) -> CSRange { + return Sym.CSSymbolGetRange(symbol) +} + +func CSSymbolGetName(_ symbol: CSSymbolRef) -> String? { + return Sym.CSSymbolGetName(symbol).map{ String(cString: $0) } +} + +func CSSymbolGetMangledName(_ symbol: CSSymbolRef) -> String? { + return Sym.CSSymbolGetMangledName(symbol).map{ String(cString: $0) } +} + +// .. CSSourceInfo ............................................................. + +func CSSourceInfoGetPath(_ sourceInfo: CSSourceInfoRef) -> String? { + return Sym.CSSourceInfoGetPath(sourceInfo).map{ String(cString: $0) } +} + +func CSSourceInfoGetLineNumber(_ sourceInfo: CSSourceInfoRef) -> UInt32 { + return Sym.CSSourceInfoGetLineNumber(sourceInfo) +} + +func CSSourceInfoGetColumn(_ sourceInfo: CSSourceInfoRef) -> UInt32 { + return Sym.CSSourceInfoGetColumn(sourceInfo) +} + +#endif // os(Darwin) diff --git a/stdlib/public/Backtracing/FramePointerUnwinder.swift b/stdlib/public/Backtracing/FramePointerUnwinder.swift new file mode 100644 index 0000000000000..d5cc7b25a61ea --- /dev/null +++ b/stdlib/public/Backtracing/FramePointerUnwinder.swift @@ -0,0 +1,152 @@ +//===--- FramePointerUnwinder.swift ---------------------------*- swift -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// +// +// Unwind the stack by chasing the frame pointer. +// +//===----------------------------------------------------------------------===// + +import Swift + +// @available(SwiftStdlib 5.1, *) +@_silgen_name("swift_task_getCurrent") +func _getCurrentAsyncTask() -> UnsafeRawPointer? + +@_spi(Unwinders) +public struct FramePointerUnwinder: Sequence, IteratorProtocol { + public typealias Context = C + public typealias MemoryReader = M + public typealias Address = MemoryReader.Address + + var pc: Address + var fp: Address + var asyncContext: Address + var first: Bool + var isAsync: Bool + + var reader: MemoryReader + + public init(context: Context, memoryReader: MemoryReader) { + pc = Address(context.programCounter) + fp = Address(context.framePointer) + first = true + isAsync = false + asyncContext = 0 + reader = memoryReader + } + + private func isAsyncFrame(_ storedFp: Address) -> Bool { + #if (os(macOS) || os(iOS) || os(watchOS)) && (arch(arm64) || arch(arm64_32) || arch(x86_64)) + // On Darwin, we borrow a bit of the frame pointer to indicate async + // stack frames + return (storedFp & (1 << 60)) != 0 && _getCurrentAsyncTask() != nil + #else + return false + #endif + } + + private func stripPtrAuth(_ address: Address) -> Address { + return Address(Context.stripPtrAuth(address: Context.Address(address))) + } + + private mutating func fetchAsyncContext() -> Bool { + let strippedFp = stripPtrAuth(fp) + + do { + asyncContext = try reader.fetch(from: Address(strippedFp - 8), + as: Address.self) + return true + } catch { + return false + } + } + + public mutating func next() -> Backtrace.Frame? { + if first { + first = false + pc = stripPtrAuth(pc) + return .programCounter(Backtrace.Address(pc)) + } + + if !isAsync { + // Try to read the next fp/pc pair + var next: Address = 0 + let strippedFp = stripPtrAuth(fp) + + if strippedFp == 0 + || !Context.isAlignedForStack(framePointer: + Context.Address(strippedFp)) { + return nil + } + + do { + pc = stripPtrAuth(try reader.fetch(from: + strippedFp + Address(MemoryLayout
.size), + as: Address.self)) + next = try reader.fetch(from: Address(strippedFp), as: Address.self) + } catch { + return nil + } + + if next <= fp { + return nil + } + + if !isAsyncFrame(next) { + fp = next + return .returnAddress(Backtrace.Address(pc)) + } + + isAsync = true + if !fetchAsyncContext() { + return nil + } + } + + // If we get here, we're in async mode + + var next: Address = 0 + let strippedCtx = stripPtrAuth(asyncContext) + + if strippedCtx == 0 { + return nil + } + + #if arch(arm64_32) + + // On arm64_32, the two pointers at the start of the context are 32-bit, + // although the stack layout is identical to vanilla arm64 + do { + var next32 = try reader.fetch(from: strippedCtx, as: UInt32.self) + var pc32 = try reader.fetch(from: strippedCtx + 4, as: UInt32.self) + + next = Address(next32) + pc = stripPtrAuth(Address(pc32)) + } catch { + return nil + } + #else + + // Otherwise it's two 64-bit words + do { + next = try reader.fetch(from: strippedCtx, as: Address.self) + pc = stripPtrAuth(try reader.fetch(from: strippedCtx + 8, as: Address.self)) + } catch { + return nil + } + + #endif + + asyncContext = next + + return .asyncResumePoint(Backtrace.Address(pc)) + } +} diff --git a/stdlib/public/Backtracing/MemoryReader.swift b/stdlib/public/Backtracing/MemoryReader.swift new file mode 100644 index 0000000000000..86b992825c184 --- /dev/null +++ b/stdlib/public/Backtracing/MemoryReader.swift @@ -0,0 +1,118 @@ +//===--- MemoryReader.swift -----------------------------------*- swift -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// +// +// Provides the ability to read memory, both in the current process and +// remotely. +// +//===----------------------------------------------------------------------===// + +import Swift + +@_implementationOnly import _SwiftBacktracingShims + +@_spi(MemoryReaders) public protocol MemoryReader { + associatedtype Address: FixedWidthInteger + + func fetch(from address: Address, + into buffer: UnsafeMutableBufferPointer) throws + + func fetch(from addr: Address, + into pointer: UnsafeMutablePointer) throws + + func fetch(from addr: Address, count: Int, as: T.Type) throws -> [T] + + func fetch(from addr: Address, as: T.Type) throws -> T +} + +extension MemoryReader { + + public func fetch(from addr: Address, + into pointer: UnsafeMutablePointer) throws { + try fetch(from: addr, + into: UnsafeMutableBufferPointer(start: pointer, count: 1)) + } + + public func fetch(from addr: Address, count: Int, as: T.Type) throws -> [T] { + let array = try Array(unsafeUninitializedCapacity: count){ + buffer, initializedCount in + + try fetch(from: addr, into: buffer) + + initializedCount = count + } + + return array + } + + public func fetch(from addr: Address, as: T.Type) throws -> T { + return try withUnsafeTemporaryAllocation(of: T.self, capacity: 1) { buf in + try fetch(from: addr, into: buf) + return buf[0] + } + } + +} + +@_spi(MemoryReaders) public struct UnsafeLocalMemoryReader: MemoryReader { + public typealias Address = UInt + + public func fetch(from address: Address, + into buffer: UnsafeMutableBufferPointer) throws { + buffer.baseAddress!.update(from: UnsafePointer(bitPattern: address)!, + count: buffer.count) + } +} + +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) +@_implementationOnly import Darwin.Mach + +@_spi(MemoryReaders) public struct MachError: Error { + var result: kern_return_t +} + +@_spi(MemoryReaders) public struct RemoteMemoryReader: MemoryReader { + public typealias Address = UInt64 + + private var task: task_t + + // Sadly we can't expose the type of this argument + public init(task: Any) { + self.task = task as! task_t + } + + public func fetch(from address: Address, + into buffer: UnsafeMutableBufferPointer) throws { + let size = mach_vm_size_t(MemoryLayout.stride * buffer.count) + var sizeOut = mach_vm_size_t(0) + let kr = mach_vm_read_overwrite(task, + mach_vm_address_t(address), + mach_vm_size_t(size), + unsafeBitCast(buffer.baseAddress, + to: mach_vm_address_t.self), + &sizeOut) + + if kr != KERN_SUCCESS { + throw MachError(result: kr) + } + } +} + +@_spi(MemoryReaders) public struct LocalMemoryReader: MemoryReader { + public typealias Address = UInt64 + + public func fetch(from address: Address, + into buffer: UnsafeMutableBufferPointer) throws { + let reader = RemoteMemoryReader(task: mach_task_self_) + return try reader.fetch(from: address, into: buffer) + } +} +#endif diff --git a/stdlib/public/Backtracing/Registers.swift b/stdlib/public/Backtracing/Registers.swift new file mode 100644 index 0000000000000..8b21697de0cbe --- /dev/null +++ b/stdlib/public/Backtracing/Registers.swift @@ -0,0 +1,586 @@ +//===--- Registers.swift - Dwarf register mapping -------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 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 +// +//===----------------------------------------------------------------------===// +// +// Holds enums that define DWARF register mappings for the architectures we +// care about. +// +//===----------------------------------------------------------------------===// + +import Swift + +// .. x86-64 ................................................................. + +// https://gitlab.com/x86-psABIs/x86-64-ABI +@_spi(Registers) public enum X86_64Register: Int, Strideable, Comparable { + + public func advanced(by n: Int) -> X86_64Register { + return X86_64Register(rawValue: self.rawValue + n)! + } + + public func distance(to other: X86_64Register) -> Int { + return other.rawValue - self.rawValue + } + + public static func < (lhs: Self, rhs: Self) -> Bool { + return lhs.rawValue < rhs.rawValue + } + +case rax = 0 +case rdx = 1 +case rcx = 2 +case rbx = 3 +case rsi = 4 +case rdi = 5 +case rbp = 6 +case rsp = 7 +case r8 = 8 +case r9 = 9 +case r10 = 10 +case r11 = 11 +case r12 = 12 +case r13 = 13 +case r14 = 14 +case r15 = 15 +case ra = 16 +case xmm0 = 17 +case xmm1 = 18 +case xmm2 = 19 +case xmm3 = 20 +case xmm4 = 21 +case xmm5 = 22 +case xmm6 = 23 +case xmm7 = 24 +case xmm8 = 25 +case xmm9 = 26 +case xmm10 = 27 +case xmm11 = 28 +case xmm12 = 29 +case xmm13 = 30 +case xmm14 = 31 +case xmm15 = 32 +case st0 = 33 +case st1 = 34 +case st2 = 35 +case st3 = 36 +case st4 = 37 +case st5 = 38 +case st6 = 39 +case st7 = 40 +case mm0 = 41 +case mm1 = 42 +case mm2 = 43 +case mm3 = 44 +case mm4 = 45 +case mm5 = 46 +case mm6 = 47 +case mm7 = 48 +case rflags = 49 +case es = 50 +case cs = 51 +case ss = 52 +case ds = 53 +case fs = 54 +case gs = 55 + // 56-57 are reserved +case fs_base = 58 +case gs_base = 59 + // 60-61 are reserved +case tr = 62 +case ldtr = 63 +case mxcsr = 64 +case fcw = 65 +case fsw = 66 +case xmm16 = 67 +case xmm17 = 68 +case xmm18 = 69 +case xmm19 = 70 +case xmm20 = 71 +case xmm21 = 72 +case xmm22 = 73 +case xmm23 = 74 +case xmm24 = 75 +case xmm25 = 76 +case xmm26 = 77 +case xmm27 = 78 +case xmm28 = 79 +case xmm29 = 80 +case xmm30 = 81 +case xmm31 = 82 + // 83-117 are reserved +case k0 = 118 +case k1 = 119 +case k2 = 120 +case k3 = 121 +case k4 = 122 +case k5 = 123 +case k6 = 124 +case k7 = 125 + // 126-129 are reserved +} + +// .. i386 ................................................................... + +// https://gitlab.com/x86-psABIs/i386-ABI +@_spi(Registers) public enum I386Register: Int, Strideable, Comparable { + + public func advanced(by n: Int) -> I386Register { + return I386Register(rawValue: self.rawValue + n)! + } + + public func distance(to other: I386Register) -> Int { + return other.rawValue - self.rawValue + } + + public static func < (lhs: Self, rhs: Self) -> Bool { + return lhs.rawValue < rhs.rawValue + } + +case eax = 0 +case ecx = 1 +case edx = 2 +case ebx = 3 +case esp = 4 +case ebp = 5 +case esi = 6 +case edi = 7 +case ra = 8 +case eflags = 9 + // 10 is reserved +case st0 = 11 +case st1 = 12 +case st2 = 13 +case st3 = 14 +case st4 = 15 +case st5 = 16 +case st6 = 17 +case st7 = 18 + // 19-20 are reserved +case xmm0 = 21 +case xmm1 = 22 +case xmm2 = 23 +case xmm3 = 24 +case xmm4 = 25 +case xmm5 = 26 +case xmm6 = 27 +case xmm7 = 28 +case mm0 = 29 +case mm1 = 30 +case mm2 = 31 +case mm3 = 32 +case mm4 = 33 +case mm5 = 34 +case mm6 = 35 +case mm7 = 36 + // 36-38 are reserved +case mxcsr = 39 +case es = 40 +case cs = 41 +case ss = 42 +case ds = 43 +case fs = 44 +case gs = 45 + // 46-47 are reserved +case tr = 48 +case ldtr = 49 + // 50-92 are reserved +case fs_base = 93 +case gs_base = 94 +} + +// .. arm64 .................................................................. + +// https://github.com/ARM-software/abi-aa/tree/main/aadwarf64 +@_spi(Registers) public enum ARM64Register: Int, Strideable, Comparable { + + public func advanced(by n: Int) -> ARM64Register { + return ARM64Register(rawValue: self.rawValue + n)! + } + + public func distance(to other: ARM64Register) -> Int { + return other.rawValue - self.rawValue + } + + public static func < (lhs: Self, rhs: Self) -> Bool { + return lhs.rawValue < rhs.rawValue + } + +case x0 = 0 +case x1 = 1 +case x2 = 2 +case x3 = 3 +case x4 = 4 +case x5 = 5 +case x6 = 6 +case x7 = 7 +case x8 = 8 +case x9 = 9 +case x10 = 10 +case x11 = 11 +case x12 = 12 +case x13 = 13 +case x14 = 14 +case x15 = 15 +case x16 = 16 +case x17 = 17 +case x18 = 18 +case x19 = 19 +case x20 = 20 +case x21 = 21 +case x22 = 22 +case x23 = 23 +case x24 = 24 +case x25 = 25 +case x26 = 26 +case x27 = 27 +case x28 = 28 +case x29 = 29 // fp +case x30 = 30 // lr +case sp = 31 // x31 +case pc = 32 +case elr_mode = 33 +case ra_sign_state = 34 +case tpidrro_el0 = 35 +case tpidr_el0 = 36 +case tpidr_el1 = 37 +case tpidr_el2 = 38 +case tpidr_el3 = 39 + // 40-45 are reserved +case vg = 46 +case ffr = 47 +case p0 = 48 +case p1 = 49 +case p2 = 50 +case p3 = 51 +case p4 = 52 +case p5 = 53 +case p6 = 54 +case p7 = 55 +case p8 = 56 +case p9 = 57 +case p10 = 58 +case p11 = 59 +case p12 = 60 +case p13 = 61 +case p14 = 62 +case p15 = 63 +case v0 = 64 +case v1 = 65 +case v2 = 66 +case v3 = 67 +case v4 = 68 +case v5 = 69 +case v6 = 70 +case v7 = 71 +case v8 = 72 +case v9 = 73 +case v10 = 74 +case v11 = 75 +case v12 = 76 +case v13 = 77 +case v14 = 78 +case v15 = 79 +case v16 = 80 +case v17 = 81 +case v18 = 82 +case v19 = 83 +case v20 = 84 +case v21 = 85 +case v22 = 86 +case v23 = 87 +case v24 = 88 +case v25 = 89 +case v26 = 90 +case v27 = 91 +case v28 = 92 +case v29 = 93 +case v30 = 94 +case v31 = 95 +case z0 = 96 +case z1 = 97 +case z2 = 98 +case z3 = 99 +case z4 = 100 +case z5 = 101 +case z6 = 102 +case z7 = 103 +case z8 = 104 +case z9 = 105 +case z10 = 106 +case z11 = 107 +case z12 = 108 +case z13 = 109 +case z14 = 110 +case z15 = 111 +case z16 = 112 +case z17 = 113 +case z18 = 114 +case z19 = 115 +case z20 = 116 +case z21 = 117 +case z22 = 118 +case z23 = 119 +case z24 = 120 +case z25 = 121 +case z26 = 122 +case z27 = 123 +case z28 = 124 +case z29 = 125 +case z30 = 126 +case z31 = 127 +} + +// .. arm .................................................................... + +// https://github.com/ARM-software/abi-aa/tree/main/aadwarf32 +@_spi(Registers) public enum ARMRegister: Int, Strideable, Comparable { + + public func advanced(by n: Int) -> ARMRegister { + return ARMRegister(rawValue: self.rawValue + n)! + } + + public func distance(to other: ARMRegister) -> Int { + return other.rawValue - self.rawValue + } + + public static func < (lhs: Self, rhs: Self) -> Bool { + return lhs.rawValue < rhs.rawValue + } + +case r0 = 0 +case r1 = 1 +case r2 = 2 +case r3 = 3 +case r4 = 4 +case r5 = 5 +case r6 = 6 +case r7 = 7 +case r8 = 8 +case r9 = 9 +case r10 = 10 +case r11 = 11 // fp +case r12 = 12 // ip - scratch register (NOT "instruction pointer") +case r13 = 13 // sp +case r14 = 14 // lr +case r15 = 15 // pc + + // Obsolescent, overlapping mappings for FPA and VFP +case old_f0_s0 = 16 +case old_f1_s1 = 17 +case old_f2_s2 = 18 +case old_f3_s3 = 19 +case old_f4_s4 = 20 +case old_f5_s5 = 21 +case old_f6_s6 = 22 +case old_f7_s7 = 23 +case old_s8 = 24 +case old_s9 = 25 +case old_s10 = 26 +case old_s11 = 27 +case old_s12 = 28 +case old_s13 = 29 +case old_s14 = 30 +case old_s15 = 31 +case old_s16 = 32 +case old_s17 = 33 +case old_s18 = 34 +case old_s19 = 35 +case old_s20 = 36 +case old_s21 = 37 +case old_s22 = 38 +case old_s23 = 39 +case old_s24 = 40 +case old_s25 = 41 +case old_s26 = 42 +case old_s27 = 43 +case old_s28 = 44 +case old_s29 = 45 +case old_s30 = 46 +case old_s31 = 47 + + // Legacy VFPv2 +case s0 = 64 +case s1 = 65 +case s2 = 66 +case s3 = 67 +case s4 = 68 +case s5 = 69 +case s6 = 70 +case s7 = 71 +case s8 = 72 +case s9 = 73 +case s10 = 74 +case s11 = 75 +case s12 = 76 +case s13 = 77 +case s14 = 78 +case s15 = 79 +case s16 = 80 +case s17 = 81 +case s18 = 82 +case s19 = 83 +case s20 = 84 +case s21 = 85 +case s22 = 86 +case s23 = 87 +case s24 = 88 +case s25 = 89 +case s26 = 90 +case s27 = 91 +case s28 = 92 +case s29 = 93 +case s30 = 94 +case s31 = 95 + + // Obsolescent FPA registers +case f0 = 96 +case f1 = 97 +case f2 = 98 +case f3 = 99 +case f4 = 100 +case f5 = 101 +case f6 = 102 +case f7 = 103 + + // Intel wireless MMX GPRs / XScale accumulators +case wcgr0_acc0 = 104 +case wcgr1_acc1 = 105 +case wcgr2_acc2 = 106 +case wcgr3_acc3 = 107 +case wcgr4_acc4 = 108 +case wcgr5_acc5 = 109 +case wcgr6_acc6 = 110 +case wcgr7_acc7 = 111 + + // Intel wireless MMX data registers +case wr0 = 112 +case wr1 = 113 +case wr2 = 114 +case wr3 = 115 +case wr4 = 116 +case wr5 = 117 +case wr6 = 118 +case wr7 = 119 +case wr8 = 120 +case wr9 = 121 +case wr10 = 122 +case wr11 = 123 +case wr12 = 124 +case wr13 = 125 +case wr14 = 126 +case wr15 = 127 + +case spsr = 128 +case spsr_fiq = 129 +case spsr_irq = 130 +case spsr_abt = 131 +case spsr_und = 132 +case spsr_svc = 133 + + // 134-142 are reserved + +case ra_auth_code = 143 + +case r8_usr = 144 +case r9_usr = 145 +case r10_usr = 146 +case r11_usr = 147 +case r12_usr = 148 +case r13_usr = 149 +case r14_usr = 150 + +case r8_fiq = 151 +case r9_fiq = 152 +case r10_fiq = 153 +case r11_fiq = 154 +case r12_fiq = 155 +case r13_fiq = 156 +case r14_fiq = 157 + +case r13_irq = 158 +case r14_irq = 159 + +case r13_abt = 160 +case r14_abt = 161 + +case r13_und = 162 +case r14_und = 163 + +case r13_svc = 164 +case r14_svc = 165 + + // 166-191 are reserved + + // Intel wqireless MMX control register +case wc0 = 192 +case wc1 = 193 +case wc2 = 194 +case wc3 = 195 +case wc4 = 196 +case wc5 = 197 +case wc6 = 198 +case wc7 = 199 + + // 200-255 are reserved + +case d0 = 256 +case d1 = 257 +case d2 = 258 +case d3 = 259 +case d4 = 260 +case d5 = 261 +case d6 = 262 +case d7 = 263 +case d8 = 264 +case d9 = 265 +case d10 = 266 +case d11 = 267 +case d12 = 268 +case d13 = 269 +case d14 = 270 +case d15 = 271 +case d16 = 272 +case d17 = 273 +case d18 = 274 +case d19 = 275 +case d20 = 276 +case d21 = 277 +case d22 = 278 +case d23 = 279 +case d24 = 280 +case d25 = 281 +case d26 = 282 +case d27 = 283 +case d28 = 284 +case d29 = 285 +case d30 = 286 +case d31 = 287 + + // 288-319 are reserved + +case tpidruro = 320 +case tpidrurw = 321 +case tpidpr = 322 +case htpidpr = 323 + + // 324-8191 are reserved + // 8192-16383 are for vendor co-processors +} + +#if arch(x86_64) +@_spi(Registers) public typealias HostRegister = X86_64Register +#elseif arch(i386) +@_spi(Registers) public typealias HostRegister = I386Register +#elseif arch(arm64) || arch(arm64_32) +@_spi(Registers) public typealias HostRegister = ARM64Register +#elseif arch(arm) +@_spi(Registers) public typealias HostRegister = ARMRegister +#endif diff --git a/stdlib/public/Backtracing/SymbolicatedBacktrace.swift b/stdlib/public/Backtracing/SymbolicatedBacktrace.swift new file mode 100644 index 0000000000000..50886c5e129d7 --- /dev/null +++ b/stdlib/public/Backtracing/SymbolicatedBacktrace.swift @@ -0,0 +1,464 @@ +//===--- Backtrace.swift --------------------------------------*- swift -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// +// +// Defines the `SymbolicatedBacktrace` struct that represents a captured +// backtrace with symbols. +// +//===----------------------------------------------------------------------===// + +import Swift + +@_implementationOnly import _SwiftBacktracingShims + +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) +@_implementationOnly import CoreFoundation +#endif + +@_silgen_name("_swift_isThunkFunction") +func _swift_isThunkFunction( + _ rawName: UnsafePointer? +) -> CBool + +/// A symbolicated backtrace +public struct SymbolicatedBacktrace: CustomStringConvertible { + /// The `Backtrace` from which this was constructed + public var backtrace: Backtrace + + /// Represents a location in source code. + /// + /// The information in this structure comes from compiler-generated + /// debug information and may not correspond to the current state of + /// the filesystem --- it might even hold a path that only works + /// from an entirely different machine. + public struct SourceLocation: CustomStringConvertible, Sendable, Hashable { + /// The path of the source file. + public var path: String + + /// The line number. + public var line: Int + + /// The column number. + public var column: Int + + /// Provide a textual description. + public var description: String { + if column > 0 && line > 0 { + return "\(path):\(line):\(column)" + } else if line > 0 { + return "\(path):\(line)" + } else { + return path + } + } + } + + /// Represents an individual frame in the backtrace. + public struct Frame: CustomStringConvertible { + /// The captured frame from the `Backtrace`. + public var captured: Backtrace.Frame + + /// The result of doing a symbol lookup for this frame. + public var symbol: Symbol? + + /// If `true`, then this frame was inlined + public var inlined: Bool = false + + /// `true` if this frame represents a Swift runtime failure. + public var isSwiftRuntimeFailure: Bool { + symbol?.isSwiftRuntimeFailure ?? false + } + + /// `true` if this frame represents a Swift thunk function. + public var isSwiftThunk: Bool { + symbol?.isSwiftThunk ?? false + } + + /// `true` if this frame is a system frame. + public var isSystem: Bool { + symbol?.isSystemFunction ?? false + } + + /// A textual description of this frame. + public var description: String { + if let symbol = symbol { + let isInlined = inlined ? " [inlined]" : "" + let isThunk = isSwiftThunk ? " [thunk]" : "" + return "\(captured)\(isInlined)\(isThunk) \(symbol)" + } else { + return captured.description + } + } + } + + /// Represents a symbol we've located + public class Symbol: CustomStringConvertible { + /// The index of the image in which the symbol for this address is located. + public var imageIndex: Int + + /// The name of the image in which the symbol for this address is located. + public var imageName: String + + /// The raw symbol name, before demangling. + public var rawName: String + + /// The demangled symbol name. + public lazy var name: String = demangleRawName() + + /// The offset from the symbol. + public var offset: Int + + /// The source location, if available. + public var sourceLocation: SourceLocation? + + /// True if this symbol represents a Swift runtime failure. + public var isSwiftRuntimeFailure: Bool { + guard let sourceLocation = sourceLocation else { + return false + } + + let symName: Substring + if rawName.hasPrefix("_") { + symName = rawName.dropFirst() + } else { + symName = rawName.dropFirst(0) + } + + return symName.hasPrefix("Swift runtime failure: ") + && sourceLocation.line == 0 + && sourceLocation.column == 0 + && sourceLocation.path.hasSuffix("") + } + + /// True if this symbol is a Swift thunk function. + public var isSwiftThunk: Bool { + return _swift_isThunkFunction(rawName) + } + + /// True if this symbol represents a system function. + /// + /// For instance, the `start` function from `dyld` on macOS is a system + /// function, and we don't need to display it under normal circumstances. + public var isSystemFunction: Bool { + #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + if rawName == "start" && imageName == "dyld" { + return true + } + if let location = sourceLocation, + location.line == 0 && location.column == 0 { + return true + } + if rawName.hasSuffix("5$mainyyFZ") { + return true + } + #endif + return false + } + + /// Construct a new Symbol. + public init(imageIndex: Int, imageName: String, + rawName: String, offset: Int, sourceLocation: SourceLocation?) { + self.imageIndex = imageIndex + self.imageName = imageName + self.rawName = rawName + self.offset = offset + self.sourceLocation = sourceLocation + } + + /// Demangle the raw name, if possible. + private func demangleRawName() -> String { + // We don't actually need this function on macOS because we're using + // CoreSymbolication, which demangles the name when it does the lookup + // anyway. We will need it for Linux and Windows though. + return rawName + } + + /// A textual description of this symbol. + public var description: String { + let symPlusOffset: String + + if offset > 0 { + symPlusOffset = "\(name) + \(offset)" + } else if offset < 0 { + symPlusOffset = "\(name) - \(-offset)" + } else { + symPlusOffset = name + } + + let location: String + if let sourceLocation = sourceLocation { + location = " at \(sourceLocation)" + } else { + location = "" + } + + return "[\(imageIndex)] \(imageName) \(symPlusOffset)\(location)" + } + } + + /// A list of captured frame information. + public var frames: [Frame] + + /// A list of images found in the process. + public var images: [Backtrace.Image] + + /// Shared cache information. + public var sharedCacheInfo: Backtrace.SharedCacheInfo + + /// True if this backtrace is a Swift runtime failure. + public var isSwiftRuntimeFailure: Bool { + guard let frame = frames.first else { return false } + return frame.isSwiftRuntimeFailure + } + + /// If this backtrace is a Swift runtime failure, return the description. + public var swiftRuntimeFailure: String? { + guard let frame = frames.first else { return nil } + if !frame.isSwiftRuntimeFailure { return nil } + + let symbolName = frame.symbol!.rawName + if symbolName.hasPrefix("_") { + return String(symbolName.dropFirst()) + } + return symbolName + } + + /// Construct a SymbolicatedBacktrace from a backtrace and a list of images. + private init(backtrace: Backtrace, images: [Backtrace.Image], + sharedCacheInfo: Backtrace.SharedCacheInfo, + frames: [Frame]) { + self.backtrace = backtrace + self.images = images + self.sharedCacheInfo = sharedCacheInfo + self.frames = frames + } + + #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + /// Convert a build ID to a CFUUIDBytes. + private static func uuidBytesFromBuildID(_ buildID: [UInt8]) -> CFUUIDBytes { + var result = CFUUIDBytes() + withUnsafeMutablePointer(to: &result) { + $0.withMemoryRebound(to: UInt8.self, + capacity: MemoryLayout.size) { + let bp = UnsafeMutableBufferPointer(start: $0, + count: MemoryLayout.size) + _ = bp.initialize(from: buildID) + } + } + return result + } + + /// Create a symbolicator. + private static func withSymbolicator(images: [Backtrace.Image], + sharedCacheInfo: Backtrace.SharedCacheInfo, + fn: (CSSymbolicatorRef) throws -> T) rethrows -> T { + let binaryImageList = images.map{ image in + BinaryImageInformation( + base: image.baseAddress, + extent: image.endOfText, + uuid: uuidBytesFromBuildID(image.buildID!), + arch: HostContext.coreSymbolicationArchitecture, + path: image.path, + relocations: [ + BinaryRelocationInformation( + base: image.baseAddress, + extent: image.endOfText, + name: "__TEXT" + ) + ], + flags: 0 + ) + } + + let symbolicator = CSSymbolicatorCreateWithBinaryImageList( + binaryImageList, 0, nil + ) + + defer { CSRelease(symbolicator) } + + return try fn(symbolicator) + } + + /// Generate a frame from a symbol and source info pair + private static func buildFrame(from capturedFrame: Backtrace.Frame, + with owner: CSSymbolOwnerRef, + isInline: Bool, + symbol: CSSymbolRef, + sourceInfo: CSSourceInfoRef, + images: [Backtrace.Image]) -> Frame { + if CSIsNull(symbol) { + return Frame(captured: capturedFrame, symbol: nil) + } + + let address = capturedFrame.originalProgramCounter + let rawName = CSSymbolGetMangledName(symbol) ?? "" + let name = CSSymbolGetName(symbol) ?? rawName + let range = CSSymbolGetRange(symbol) + + let location: SourceLocation? + + if !CSIsNull(sourceInfo) { + let path = CSSourceInfoGetPath(sourceInfo) ?? "" + let line = CSSourceInfoGetLineNumber(sourceInfo) + let column = CSSourceInfoGetColumn(sourceInfo) + + location = SourceLocation( + path: path, + line: Int(line), + column: Int(column) + ) + } else { + location = nil + } + + let imageBase = CSSymbolOwnerGetBaseAddress(owner) + var imageIndex = -1 + var imageName = "" + for (ndx, image) in images.enumerated() { + if image.baseAddress == imageBase { + imageIndex = ndx + imageName = image.name + break + } + } + + let theSymbol = Symbol(imageIndex: imageIndex, + imageName: imageName, + rawName: rawName, + offset: Int(address - range.location), + sourceLocation: location) + theSymbol.name = name + + return Frame(captured: capturedFrame, symbol: theSymbol, inlined: isInline) + } + #endif + + /// Actually symbolicate. + internal static func symbolicate(backtrace: Backtrace, + images: [Backtrace.Image]?, + sharedCacheInfo: Backtrace.SharedCacheInfo?, + showInlineFrames: Bool) + -> SymbolicatedBacktrace? { + + let theImages: [Backtrace.Image] + if let images = images { + theImages = images + } else if let images = backtrace.images { + theImages = images + } else { + theImages = Backtrace.captureImages() + } + + let theCacheInfo: Backtrace.SharedCacheInfo + if let sharedCacheInfo = sharedCacheInfo { + theCacheInfo = sharedCacheInfo + } else if let sharedCacheInfo = backtrace.sharedCacheInfo { + theCacheInfo = sharedCacheInfo + } else { + theCacheInfo = Backtrace.captureSharedCacheInfo() + } + + var frames: [Frame] = [] + + #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + withSymbolicator(images: theImages, + sharedCacheInfo: theCacheInfo) { symbolicator in + for frame in backtrace.frames { + switch frame { + case .omittedFrames(_), .truncated: + frames.append(Frame(captured: frame, symbol: nil)) + default: + let address = frame.adjustedProgramCounter + let owner + = CSSymbolicatorGetSymbolOwnerWithAddressAtTime(symbolicator, + address, + kCSBeginningOfTime) + + if CSIsNull(owner) { + frames.append(Frame(captured: frame, symbol: nil)) + } else if showInlineFrames { + // These present in *reverse* order (i.e. the real one first, + // then the inlined frames from callee to caller). + let pos = frames.count + var first = true + + _ = CSSymbolOwnerForEachStackFrameAtAddress(owner, address){ + symbol, sourceInfo in + + frames.insert(buildFrame(from: frame, + with: owner, + isInline: !first, + symbol: symbol, + sourceInfo: sourceInfo, + images: theImages), + at: pos) + + first = false + } + } else { + let symbol = CSSymbolOwnerGetSymbolWithAddress(owner, address) + let sourceInfo = CSSymbolOwnerGetSourceInfoWithAddress(owner, + address) + + frames.append(buildFrame(from: frame, + with: owner, + isInline: false, + symbol: symbol, + sourceInfo: sourceInfo, + images: theImages)) + } + } + } + } + #else + frames = backtrace.frames.map{ Frame(captured: $0, symbol: nil) } + #endif + + return SymbolicatedBacktrace(backtrace: backtrace, + images: theImages, + sharedCacheInfo: theCacheInfo, + frames: frames) + } + + /// Provide a textual version of the backtrace. + public var description: String { + var lines: [String] = [] + + var n = 0 + for frame in frames { + lines.append("\(n)\t\(frame)") + switch frame.captured { + case let .omittedFrames(count): + n += count + default: + n += 1 + } + } + + lines.append("") + lines.append("Images:") + lines.append("") + for (n, image) in images.enumerated() { + lines.append("\(n)\t\(image)") + } + + #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) + lines.append("") + lines.append("Shared Cache:") + lines.append("") + lines.append(" UUID: \(hex(sharedCacheInfo.uuid))") + lines.append(" Base: \(hex(sharedCacheInfo.baseAddress))") + lines.append(" Active: \(!sharedCacheInfo.noCache)") + #endif + + return lines.joined(separator: "\n") + } +} diff --git a/stdlib/public/Backtracing/Utils.swift b/stdlib/public/Backtracing/Utils.swift new file mode 100644 index 0000000000000..5913f047c3843 --- /dev/null +++ b/stdlib/public/Backtracing/Utils.swift @@ -0,0 +1,32 @@ +//===--- Utils.swift - Utility functions ----------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// +// +// Utility functions that are used in the backtracing library. +// +//===----------------------------------------------------------------------===// + +import Swift + +internal func hex(_ value: T, + withPrefix: Bool = true) -> String { + let digits = String(value, radix: 16) + let padTo = value.bitWidth / 4 + let padding = digits.count >= padTo ? "" : String(repeating: "0", + count: padTo - digits.count) + let prefix = withPrefix ? "0x" : "" + + return "\(prefix)\(padding)\(digits)" +} + +internal func hex(_ bytes: [UInt8]) -> String { + return bytes.map{ hex($0, withPrefix: false) }.joined(separator: "") +} diff --git a/stdlib/public/Backtracing/get-cpu-context.S b/stdlib/public/Backtracing/get-cpu-context.S new file mode 100644 index 0000000000000..640961cbc0223 --- /dev/null +++ b/stdlib/public/Backtracing/get-cpu-context.S @@ -0,0 +1,142 @@ +//===--- get-cpu-context.S - Low-level functions to capture registers -----===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 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 +// +//===----------------------------------------------------------------------===// +// +// Saves the necessary registers to an appropriate Context structure. +// +//===----------------------------------------------------------------------===// + + // On Apple platforms, we need an extra underscore +#if __APPLE__ + #define FN(x) _##x +#else + #define FN(x) x +#endif + + .text + .globl FN(_swift_get_cpu_context) + +// .. x86-64 ................................................................... + +#if defined(__x86_64__) + // On entry, rax contains the pointer to the x86_64_gprs +FN(_swift_get_cpu_context): + movq %rax, (%rax) + movq %rdx, 8(%rax) + movq %rcx, 16(%rax) + movq %rbx, 24(%rax) + movq %rsi, 32(%rax) + movq %rdi, 40(%rax) + movq %rbp, 48(%rax) + leaq 8(%rsp), %rdx + movq %rdx, 56(%rax) + movq %r8, 64(%rax) + movq %r9, 72(%rax) + movq %r10, 80(%rax) + movq %r11, 88(%rax) + movq %r12, 96(%rax) + movq %r13, 104(%rax) + movq %r14, 112(%rax) + movq %r15, 120(%rax) + pushf + pop %rdx + movq %rdx, 128(%rax) + movw %cs, %dx + movw %dx, 136(%rax) + movw %fs, %dx + movw %dx, 138(%rax) + movw %gs, %dx + movw %dx, 140(%rax) + movq (%rsp), %rdx + movq %rdx, 144(%rax) + movq $0x1fffff, 152(%rax) + ret +#endif + +// .. i386 ..................................................................... + +#if defined(__i386__) + // On entry, 8(%esp) contains the pointer to the i386_gprs +FN(_swift_get_cpu_context): + push %eax + movl 8(%esp), %eax + movl %ecx, 4(%eax) + movl %edx, 8(%eax) + movl %ebx, 12(%eax) + movl %esi, 16(%eax) + movl %edi, 20(%eax) + movl %ebp, 24(%eax) + leal 8(%esp), %edx + movl %edx, 28(%eax) + pushf + pop %edx + movl %edx, 32(%eax) + movw %ss, %dx + movw %dx, 36(%eax) + movw %cs, %dx + movw %dx, 38(%eax) + movw %ds, %dx + movw %dx, 40(%eax) + movw %es, %dx + movw %dx, 42(%eax) + movw %fs, %dx + movw %dx, 44(%eax) + movw %gs, %dx + movw %dx, 46(%eax) + movl (%esp), %dx + movl %edx, (%eax) + movl $0xffff, 48(%eax) + popl %eax + ret +#endif + +// .. ARM64 .................................................................... + +#if defined(__aarch64__) + .p2align 2 + // On entry, x8 contains a pointer to the arm64_gprs +FN(_swift_get_cpu_context): + stp x0, x1, [x8, #0x00] + stp x2, x3, [x8, #0x10] + stp x4, x5, [x8, #0x20] + stp x6, x7, [x8, #0x30] + stp x8, x9, [x8, #0x40] + stp x10, x11, [x8, #0x50] + stp x12, x13, [x8, #0x60] + stp x14, x15, [x8, #0x70] + stp x16, x17, [x8, #0x80] + stp x18, x19, [x8, #0x90] + stp x20, x21, [x8, #0xa0] + stp x22, x23, [x8, #0xb0] + stp x24, x25, [x8, #0xc0] + stp x26, x27, [x8, #0xd0] + stp x28, x29, [x8, #0xe0] + mov x1, sp + stp x30, x1, [x8, #0xf0] + str x30, [x8, #0x100] + mov x1, #0x1ffffffff + str x1, [x8, #0x108] + ret +#endif + +// .. 32-bit ARM ............................................................... + +#if defined(__arm__) + .p2align 2 + // On entry, r0 contains a pointer to the arm_gprs +FN(_swift_get_cpu_context): + stm r0, {r0-r14} + str lr, [r0, #0x3c] + mov r1, #0xffff + str r1, [r0, #0x40] + bx lr +#endif + diff --git a/stdlib/public/CMakeLists.txt b/stdlib/public/CMakeLists.txt index cc8f6401e4793..867ce013b05f4 100644 --- a/stdlib/public/CMakeLists.txt +++ b/stdlib/public/CMakeLists.txt @@ -155,6 +155,8 @@ if(SWIFT_BUILD_STDLIB) if(SWIFT_ENABLE_EXPERIMENTAL_OBSERVATION) add_subdirectory(Observation) endif() + + add_subdirectory(Backtracing) endif() if(SWIFT_BUILD_STDLIB OR SWIFT_BUILD_REMOTE_MIRROR) diff --git a/stdlib/public/SwiftShims/swift/shims/CMakeLists.txt b/stdlib/public/SwiftShims/swift/shims/CMakeLists.txt index b0df12ada5d26..1e548f16071fa 100644 --- a/stdlib/public/SwiftShims/swift/shims/CMakeLists.txt +++ b/stdlib/public/SwiftShims/swift/shims/CMakeLists.txt @@ -21,6 +21,7 @@ set(sources ThreadLocalStorage.h UnicodeData.h Visibility.h + _SwiftBacktracing.h _SwiftConcurrency.h _SwiftDistributed.h _SwiftRuntime.h diff --git a/stdlib/public/SwiftShims/swift/shims/_SwiftBacktracing.h b/stdlib/public/SwiftShims/swift/shims/_SwiftBacktracing.h index 46f1e0833690d..2d5ee2a5c6322 100644 --- a/stdlib/public/SwiftShims/swift/shims/_SwiftBacktracing.h +++ b/stdlib/public/SwiftShims/swift/shims/_SwiftBacktracing.h @@ -19,6 +19,20 @@ #include +#ifdef __APPLE__ +#include +#endif + +#if TARGET_OS_OSX +#include +#include + +#include +#include + +#include +#endif + #ifdef __cplusplus namespace swift { extern "C" { @@ -31,6 +45,221 @@ struct CrashInfo { uint64_t mctx; }; +// .. Processor specifics ...................................................... + +struct x86_64_gprs { + uint64_t _r[16]; + uint64_t rflags; + uint16_t cs, fs, gs, _pad0; + uint64_t rip; + uint64_t valid; +}; + +struct i386_gprs { + uint32_t _r[8]; + uint32_t eflags; + uint16_t segreg[6]; + uint32_t eip; + uint32_t valid; +}; + +struct arm64_gprs { + uint64_t _x[32]; + uint64_t pc; + uint64_t valid; +}; + +struct arm_gprs { + uint32_t _r[16]; + uint32_t valid; +}; + +// .. Darwin specifics ......................................................... + +#if TARGET_OS_OSX + +/* Darwin thread states. We can't import these from the system header because + it uses all kinds of macros and the Swift importer can't cope with that. + So declare them here in a form it can understand. */ +#define ARM_THREAD_STATE64 6 +struct darwin_arm64_thread_state { + uint64_t _x[29]; + uint64_t fp; + uint64_t lr; + uint64_t sp; + uint64_t pc; + uint32_t cpsr; + uint32_t __pad; +}; + +struct darwin_arm64_exception_state { + uint64_t far; + uint32_t esr; + uint32_t exception; +}; + +struct darwin_arm64_mcontext { + struct darwin_arm64_exception_state es; + struct darwin_arm64_thread_state ss; + // followed by NEON state (which we don't care about) +}; + +#define x86_THREAD_STATE64 4 +struct darwin_x86_64_thread_state { + uint64_t rax; + uint64_t rbx; + uint64_t rcx; + uint64_t rdx; + uint64_t rdi; + uint64_t rsi; + uint64_t rbp; + uint64_t rsp; + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + uint64_t rip; + uint64_t rflags; + uint64_t cs; + uint64_t fs; + uint64_t gs; +}; + +struct darwin_x86_64_exception_state { + uint16_t trapno; + uint16_t cpu; + uint32_t err; + uint64_t faultvaddr; +}; + +struct darwin_x86_64_mcontext { + struct darwin_x86_64_exception_state es; + struct darwin_x86_64_thread_state ss; + // followed by FP/AVX/AVX512 state (which we don't care about) +}; + +/* DANGER! These are SPI. They may change (or vanish) at short notice, may + not work how you expect, and are generally dangerous to use. */ +// ###FIXME: Remove the 0 +#if 0 && __has_include() + #include +#else +struct dyld_process_cache_info { + uuid_t cacheUUID; + uint64_t cacheBaseAddress; + bool noCache; + bool privateCache; +}; +typedef struct dyld_process_cache_info dyld_process_cache_info; +typedef const struct dyld_process_info_base* dyld_process_info; + +extern dyld_process_info _dyld_process_info_create(task_t task, uint64_t timestamp, kern_return_t* kernelError); +extern void _dyld_process_info_release(dyld_process_info info); +extern void _dyld_process_info_retain(dyld_process_info info); +extern void _dyld_process_info_get_cache(dyld_process_info info, dyld_process_cache_info* cacheInfo); +extern void _dyld_process_info_for_each_image(dyld_process_info info, void (^callback)(uint64_t machHeaderAddress, const uuid_t uuid, const char* path)); +extern void _dyld_process_info_for_each_segment(dyld_process_info info, uint64_t machHeaderAddress, void (^callback)(uint64_t segmentAddress, uint64_t segmentSize, const char* segmentName)); +#endif + +/* DANGER! CoreSymbolication is a private framework. This is all SPI. */ +// ###FIXME: Remove the 0 +struct _CSArchitecture { + cpu_type_t cpu_type; + cpu_subtype_t cpu_subtype; +}; + +typedef struct _CSArchitecture CSArchitecture; + +static const CSArchitecture kCSArchitectureI386 = { CPU_TYPE_I386, CPU_SUBTYPE_I386_ALL }; +static const CSArchitecture kCSArchitectureX86_64 = { CPU_TYPE_I386|CPU_ARCH_ABI64, CPU_SUBTYPE_I386_ALL }; +static const CSArchitecture kCSArchitectureArm64 = { CPU_TYPE_ARM | CPU_ARCH_ABI64, CPU_SUBTYPE_ARM64_ALL }; +static const CSArchitecture kCSArchitectureArm64_32 = { CPU_TYPE_ARM | CPU_ARCH_ABI64_32, CPU_SUBTYPE_ARM64_ALL }; +static const CSArchitecture kCSArchitectureArmV7K = { CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7K }; + +typedef struct _CSBinaryRelocationInformation { + mach_vm_address_t base; + mach_vm_address_t extent; + char name[17]; +} CSBinaryRelocationInformation; + +typedef struct _CSBinaryImageInformation { + mach_vm_address_t base; + mach_vm_address_t extent; + CFUUIDBytes uuid; + CSArchitecture arch; + const char *path; + CSBinaryRelocationInformation *relocations; + uint32_t relocationCount; + uint32_t flags; +} CSBinaryImageInformation; + +typedef uint64_t CSMachineTime; + +static const CSMachineTime kCSBeginningOfTime = 0; +static const CSMachineTime kCSEndOfTime = INT64_MAX; +static const CSMachineTime kCSNow = INT64_MAX + 1ULL; +static const CSMachineTime kCSAllTimes = INT64_MAX + 2ULL; + +struct _CSTypeRef { + uintptr_t _opaque_1; + uintptr_t _opaque_2; +}; + +typedef struct _CSTypeRef CSTypeRef; + +typedef CSTypeRef CSNullRef; +typedef CSTypeRef CSSymbolicatorRef; +typedef CSTypeRef CSSymbolOwnerRef; +typedef CSTypeRef CSSymbolRef; +typedef CSTypeRef CSSourceInfoRef; + +static const CSNullRef kCSNull = { 0, 0 }; + +typedef void (^CSSymbolOwnerIterator)(CSSymbolOwnerRef owner); +typedef void (^CSStackFrameIterator)(CSSymbolRef symbol, CSSourceInfoRef info); + +typedef struct _CSNotificationData { + CSSymbolicatorRef symbolicator; + union { + struct Ping { + uint32_t value; + } ping; + + struct DyldLoad { + CSSymbolOwnerRef symbolOwner; + } dyldLoad; + + struct DyldUnload { + CSSymbolOwnerRef symbolOwner; + } dyldUnload; + } u; +} CSNotificationData; + +typedef void (^CSNotificationBlock)(uint32_t type, CSNotificationData data); + +struct _CSRange { + mach_vm_address_t location; + mach_vm_size_t length; +}; + +typedef struct _CSRange CSRange; + +/* DANGER! This is also SPI */ +enum { + kCRSanitizePathGlobLocalHomeDirectories = 1, + kCRSanitizePathGlobLocalVolumes = 2, + kCRSanitizePathGlobAllTypes = 0xff, + + kCRSanitizePathNormalize = 0x100 << 0, + kCRSanitizePathKeepFile = 0x100 << 1, +}; + +#endif // TARGET_OS_OSX + #ifdef __cplusplus } // extern "C" } // namespace swift diff --git a/stdlib/public/SwiftShims/swift/shims/module.modulemap b/stdlib/public/SwiftShims/swift/shims/module.modulemap index 57e872cd2594e..cfc4d3990ba36 100644 --- a/stdlib/public/SwiftShims/swift/shims/module.modulemap +++ b/stdlib/public/SwiftShims/swift/shims/module.modulemap @@ -26,6 +26,10 @@ module _SwiftConcurrencyShims { header "_SwiftConcurrency.h" } +module _SwiftBacktracingShims { + header "_SwiftBacktracing.h" +} + module SwiftOverlayShims { header "LibcOverlayShims.h" export * diff --git a/test/Backtracing/BacktraceWithLimit.swift b/test/Backtracing/BacktraceWithLimit.swift new file mode 100644 index 0000000000000..d84738cbd24c9 --- /dev/null +++ b/test/Backtracing/BacktraceWithLimit.swift @@ -0,0 +1,45 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -parse-as-library -Onone -o %t/BacktraceWithLimit +// RUN: %target-codesign %t/BacktraceWithLimit +// RUN: %target-run %t/BacktraceWithLimit | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: OS=macosx || OS=ios + +import _Backtracing + +func doFrames(_ count: Int) { + if count <= 0 { + let backtrace = try! Backtrace.capture(limit: 10, top: 0) + + print(backtrace) + } else { + doFrames(count - 1) + } +} + +@main +struct BacktraceWithLimit { + static func main() { + // CHECK: 0{{[ \t]+}}0x{{[0-9a-f]+}} [ra] + // CHECK-NEXT: 1{{[ \t]+}}0x{{[0-9a-f]+}} [ra] + // CHECK-NEXT: 2{{[ \t]+}}0x{{[0-9a-f]+}} [ra] + // CHECK-NEXT: 3{{[ \t]+}}0x{{[0-9a-f]+}} [ra] + // CHECK-NEXT: 4{{[ \t]+}}0x{{[0-9a-f]+}} [ra] + // CHECK-NEXT: 5{{[ \t]+}}0x{{[0-9a-f]+}} [ra] + // CHECK-NEXT: 6{{[ \t]+}}0x{{[0-9a-f]+}} [ra] + // CHECK-NEXT: 7{{[ \t]+}}0x{{[0-9a-f]+}} [ra] + // CHECK-NEXT: 8{{[ \t]+}}0x{{[0-9a-f]+}} [ra] + // CHECK-NEXT: 9{{[ \t]+}}... + doFrames(1000) + + print("") + + // CHECK: 0{{[ \t]+}}0x{{[0-9a-f]+}} [ra] + // CHECK-NEXT: 1{{[ \t]+}}0x{{[0-9a-f]+}} [ra] + // CHECK-NEXT: 2{{[ \t]+}}0x{{[0-9a-f]+}} [ra] + // CHECK-NEXT: 3{{[ \t]+}}0x{{[0-9a-f]+}} [ra] + // CHECK-NEXT: 4{{[ \t]+}}0x{{[0-9a-f]+}} [ra] + doFrames(5) + } +} diff --git a/test/Backtracing/BacktraceWithLimitAndTop.swift b/test/Backtracing/BacktraceWithLimitAndTop.swift new file mode 100644 index 0000000000000..30c9818a39f33 --- /dev/null +++ b/test/Backtracing/BacktraceWithLimitAndTop.swift @@ -0,0 +1,60 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -parse-as-library -Onone -o %t/BacktraceWithLimitAndTop +// RUN: %target-codesign %t/BacktraceWithLimitAndTop +// RUN: %target-run %t/BacktraceWithLimitAndTop | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: OS=macosx || OS=ios + +import _Backtracing + +func doFrames(_ count: Int, limit: Int, top: Int) { + if count <= 0 { + let backtrace = try! Backtrace.capture(limit: limit, top: top) + + print(backtrace) + } else { + doFrames(count - 1, limit: limit, top: top) + } +} + +@main +struct BacktraceWithTop { + static func main() { + // CHECK: 0{{[ \t]+}}0x{{[0-9a-f]+}} [ra] + // CHECK-NEXT: 1{{[ \t]+}}0x{{[0-9a-f]+}} [ra] + // CHECK-NEXT: 2{{[ \t]+}}0x{{[0-9a-f]+}} [ra] + // CHECK-NEXT: 3{{[ \t]+}}0x{{[0-9a-f]+}} [ra] + // CHECK-NEXT: 4{{[ \t]+}}0x{{[0-9a-f]+}} [ra] + // CHECK-NEXT: 5{{[ \t]+}}... + // CHECK-NEXT: {{[0-9]+[ \t]+}}0x{{[0-9a-f]+}} [ra] + // CHECK-NEXT: {{[0-9]+[ \t]+}}0x{{[0-9a-f]+}} [ra] + // CHECK-NEXT: {{[0-9]+[ \t]+}}0x{{[0-9a-f]+}} [ra] + // CHECK-NEXT: {{[0-9]+[ \t]+}}0x{{[0-9a-f]+}} [ra] + doFrames(1000, limit: 10, top: 4) + + print("") + + // CHECK: 0{{[ \t]+}}... + // CHECK-NEXT: {{[0-9]+[ \t]+}}0x{{[0-9a-f]+}} [ra] + // CHECK-NEXT: {{[0-9]+[ \t]+}}0x{{[0-9a-f]+}} [ra] + // CHECK-NEXT: {{[0-9]+[ \t]+}}0x{{[0-9a-f]+}} [ra] + // CHECK-NEXT: {{[0-9]+[ \t]+}}0x{{[0-9a-f]+}} [ra] + // CHECK-NEXT: {{[0-9]+[ \t]+}}0x{{[0-9a-f]+}} [ra] + // CHECK-NEXT: {{[0-9]+[ \t]+}}0x{{[0-9a-f]+}} [ra] + // CHECK-NEXT: {{[0-9]+[ \t]+}}0x{{[0-9a-f]+}} [ra] + // CHECK-NEXT: {{[0-9]+[ \t]+}}0x{{[0-9a-f]+}} [ra] + // CHECK-NEXT: {{[0-9]+[ \t]+}}0x{{[0-9a-f]+}} [ra] + doFrames(1000, limit: 10, top: 10) + + print("") + + // CHECK: 0{{[ \t]+}}0x{{[0-9a-f]+}} [ra] + // CHECK-NEXT: 1{{[ \t]+}}0x{{[0-9a-f]+}} [ra] + // CHECK-NEXT: 2{{[ \t]+}}0x{{[0-9a-f]+}} [ra] + // CHECK-NEXT: 3{{[ \t]+}}0x{{[0-9a-f]+}} [ra] + // CHECK-NEXT: 4{{[ \t]+}}0x{{[0-9a-f]+}} [ra] + // CHECK-NEXT: 5{{[ \t]+}}0x{{[0-9a-f]+}} [ra] + doFrames(6, limit: 30, top: 4) + } +} diff --git a/test/Backtracing/SimpleAsyncBacktrace.swift b/test/Backtracing/SimpleAsyncBacktrace.swift new file mode 100644 index 0000000000000..573cea420e49c --- /dev/null +++ b/test/Backtracing/SimpleAsyncBacktrace.swift @@ -0,0 +1,54 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -g -parse-as-library -Onone -o %t/SimpleAsyncBacktrace +// RUN: %target-codesign %t/SimpleAsyncBacktrace +// RUN: %target-run %t/SimpleAsyncBacktrace | %FileCheck %s + +// REQUIRES: concurrency +// REQUIRES: executable_test +// REQUIRES: OS=macosx || OS=iOS || OS=watchOS || OS=tvOS +// REQUIRES: concurrency_runtime +// UNSUPPORTED: back_deployment_runtime + +import _Backtracing + +@available(SwiftStdlib 5.1, *) +func level1() async { + await level2() +} + +@available(SwiftStdlib 5.1, *) +func level2() async { + level3() +} + +@available(SwiftStdlib 5.1, *) +func level3() { + level4() +} + +@available(SwiftStdlib 5.1, *) +func level4() { + level5() +} + +@available(SwiftStdlib 5.1, *) +func level5() { + let backtrace = try! Backtrace.capture() + + // CHECK: 0{{[ \t]+}}0x{{[0-9a-f]+}} [ra] + // CHECK-NEXT: 1{{[ \t]+}}0x{{[0-9a-f]+}} [ra] + // CHECK-NEXT: 2{{[ \t]+}}0x{{[0-9a-f]+}} [ra] + // CHECK-NEXT: 3{{[ \t]+}}0x{{[0-9a-f]+}} [ra] + // CHECK-NEXT: 4{{[ \t]+}}0x{{[0-9a-f]+}} [async] + // CHECK-NEXT: 5{{[ \t]+}}0x{{[0-9a-f]+}} [async] + + print(backtrace) +} + +@available(SwiftStdlib 5.1, *) +@main +struct SimpleAsyncBacktrace { + static func main() async { + await level1() + } +} diff --git a/test/Backtracing/SimpleBacktrace.swift b/test/Backtracing/SimpleBacktrace.swift new file mode 100644 index 0000000000000..1612fa9088e7d --- /dev/null +++ b/test/Backtracing/SimpleBacktrace.swift @@ -0,0 +1,45 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -Xfrontend -parse-as-library -Onone -o %t/SimpleBacktrace +// RUN: %target-codesign %t/SimpleBacktrace +// RUN: %target-run %t/SimpleBacktrace | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: OS=macosx || OS=iOS || OS=watchOS || OS=tvOS + +import _Backtracing + +func level1() { + level2() +} + +func level2() { + level3() +} + +func level3() { + level4() +} + +func level4() { + level5() +} + +func level5() { + let backtrace = try! Backtrace.capture() + + // CHECK: 0{{[ \t]+}}0x{{[0-9a-f]+}} [ra] + // CHECK-NEXT: 1{{[ \t]+}}0x{{[0-9a-f]+}} [ra] + // CHECK-NEXT: 2{{[ \t]+}}0x{{[0-9a-f]+}} [ra] + // CHECK-NEXT: 3{{[ \t]+}}0x{{[0-9a-f]+}} [ra] + // CHECK-NEXT: 4{{[ \t]+}}0x{{[0-9a-f]+}} [ra] + // CHECK-NEXT: 5{{[ \t]+}}0x{{[0-9a-f]+}} [ra] + + print(backtrace) +} + +@main +struct SimpleBacktrace { + static func main() { + level1() + } +} diff --git a/test/lit.cfg b/test/lit.cfg index 6b5b3f6af1a08..a3146263f50ad 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -1093,7 +1093,8 @@ if run_vendor == 'apple': "swiftStdlibCollectionUnittest", "swiftSwiftPrivateLibcExtras", "swiftSwiftPrivate", "swiftDarwin", "swiftSwiftPrivateThreadExtras", - "swiftSwiftOnoneSupport", "swift_Concurrency"]: + "swiftSwiftOnoneSupport", "swift_Concurrency", + "swift_Backtracing"]: swift_execution_tests_extra_flags += ' -Xlinker -l%s'% library swift_native_clang_tools_path = lit_config.params.get('swift_native_clang_tools_path', None) @@ -1853,6 +1854,13 @@ config.substitutions.append(('%concurrency_module', concurrency_module)) config.substitutions.append(('%/concurrency_module', '/'.join(os.path.normpath(concurrency_module).split(os.sep)))) +# Add 'backtracing_module' as the path to the _Backtracing .swiftmodule file +backtracing_module = os.path.join(stdlib_dir, "_Backtracing.swiftmodule", + target_specific_module_triple + ".swiftmodule") +config.substitutions.append(('%backtracing_module', backtracing_module)) +config.substitutions.append(('%/backtracing_module', + '/'.join(os.path.normpath(backtracing_module).split(os.sep)))) + # Add 'distributed_module' as the path to the Distributed .swiftmodule file distributed_module = os.path.join(stdlib_dir, "Distributed.swiftmodule", target_specific_module_triple + ".swiftmodule") From 433895b1e00e405d92cc9a9cffb5418719049cde Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Thu, 2 Mar 2023 13:00:43 +0000 Subject: [PATCH 07/22] [Backtracing] Fix Windows assembler related problem. We need to enable MASM/MARMASM and we need to add a get-cpu-context.asm file for it to build. rdar://104336548 --- CMakeLists.txt | 20 +++++++++++++++++-- include/swift/Runtime/Backtrace.h | 10 ++++++++++ stdlib/public/Backtracing/CMakeLists.txt | 5 +++++ stdlib/public/Backtracing/get-cpu-context.asm | 2 ++ 4 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 stdlib/public/Backtracing/get-cpu-context.asm diff --git a/CMakeLists.txt b/CMakeLists.txt index d1c2fb1e147ef..49cf0395cd83d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,7 +43,22 @@ else() set(CMAKE_JOB_POOL_LINK local_jobs) endif() -ENABLE_LANGUAGE(C) +enable_language(C) +enable_language(CXX) + +# On Windows, use MASM or MARMASM +set(SWIFT_ASM_DIALECT ASM) +set(SWIFT_ASM_EXT S) +if(CMAKE_SYSTEM_NAME STREQUAL Windows) + if(CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64") + set(SWIFT_ASM_DIALECT ASM_MARMASM) + else() + set(SWIFT_ASM_DIALECT ASM_MASM) + endif() + set(SWIFT_ASM_EXT asm) +endif() + +enable_language(${SWIFT_ASM_DIALECT}) # Use C++14. set(CMAKE_CXX_STANDARD 14 CACHE STRING "C++ standard to conform to") @@ -613,7 +628,7 @@ if("${CMAKE_SOURCE_DIR}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}") endif() if(SWIFT_BUILT_STANDALONE) - project(Swift C CXX ASM) + project(Swift C CXX ${SWIFT_ASM_DIALECT}) endif() if(MSVC OR "${CMAKE_SIMULATE_ID}" STREQUAL MSVC) @@ -683,6 +698,7 @@ execute_process(COMMAND ${CMAKE_MAKE_PROGRAM} ${version_flag} message(STATUS "CMake Make Program (${CMAKE_MAKE_PROGRAM}) Version: ${_CMAKE_MAKE_PROGRAM_VERSION}") message(STATUS "C Compiler (${CMAKE_C_COMPILER}) Version: ${CMAKE_C_COMPILER_VERSION}") message(STATUS "C++ Compiler (${CMAKE_CXX_COMPILER}) Version: ${CMAKE_CXX_COMPILER_VERSION}") +message(STATUS "Assembler (${CMAKE_${SWIFT_ASM_DIALECT}_COMPILER}) Version: ${CMAKE_${SWIFT_ASM_DIALECT}_COMPILER_VERSION}") if (CMAKE_Swift_COMPILER) message(STATUS "Swift Compiler (${CMAKE_Swift_COMPILER}) Version: ${CMAKE_Swift_COMPILER_VERSION}") else() diff --git a/include/swift/Runtime/Backtrace.h b/include/swift/Runtime/Backtrace.h index db69df602c4c3..7eac721f13463 100644 --- a/include/swift/Runtime/Backtrace.h +++ b/include/swift/Runtime/Backtrace.h @@ -24,6 +24,16 @@ #include +#ifdef _WIN32 +// For DWORD +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include + +// For wchar_t +#include +#endif + #ifdef __cplusplus namespace swift { namespace runtime { diff --git a/stdlib/public/Backtracing/CMakeLists.txt b/stdlib/public/Backtracing/CMakeLists.txt index 9ae57f103b432..92275a709ed9c 100644 --- a/stdlib/public/Backtracing/CMakeLists.txt +++ b/stdlib/public/Backtracing/CMakeLists.txt @@ -25,7 +25,12 @@ set(BACKTRACING_SOURCES SymbolicatedBacktrace.swift Utils.swift + get-cpu-context.${SWIFT_ASM_EXT} +) + +set(LLVM_OPTIONAL_SOURCES get-cpu-context.S + get-cpu-context.asm ) add_swift_target_library(swift_Backtracing ${SWIFT_STDLIB_LIBRARY_BUILD_TYPES} IS_STDLIB diff --git a/stdlib/public/Backtracing/get-cpu-context.asm b/stdlib/public/Backtracing/get-cpu-context.asm new file mode 100644 index 0000000000000..f42f72ebdd83d --- /dev/null +++ b/stdlib/public/Backtracing/get-cpu-context.asm @@ -0,0 +1,2 @@ +;;; Placeholder for Windows assembly code +;;; This uses different syntax from everywhere else :-( From 3b2e863c5651eeba5215a5898ffcd1135c95a8f5 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Thu, 2 Mar 2023 15:35:17 +0000 Subject: [PATCH 08/22] [Backtracing][Windows] Use CRT, not MSVCRT. The correct name for the runtime library appears to be CRT, not MSVCRT. rdar://104336548 --- stdlib/public/Backtracing/BacktraceFormatter.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/public/Backtracing/BacktraceFormatter.swift b/stdlib/public/Backtracing/BacktraceFormatter.swift index 8c56a551d8799..6407da9630cf8 100644 --- a/stdlib/public/Backtracing/BacktraceFormatter.swift +++ b/stdlib/public/Backtracing/BacktraceFormatter.swift @@ -20,7 +20,7 @@ import Swift #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) @_implementationOnly import Darwin #elseif os(Windows) -@_implementationOnly import MSVCRT +@_implementationOnly import CRT #elseif os(Linux) @_implementationOnly import Glibc #endif From 45f08482bb6946ee09378326b7617ad02c05ef73 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Fri, 3 Mar 2023 00:06:08 +0000 Subject: [PATCH 09/22] [Backtracing] Fix OS conditionals so iOS builds work. Some of the `TARGET` and `os()` conditionals needed to be updated. rdar://104336548 --- stdlib/public/Backtracing/Context.swift | 2 +- stdlib/public/SwiftShims/swift/shims/_SwiftBacktracing.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/stdlib/public/Backtracing/Context.swift b/stdlib/public/Backtracing/Context.swift index 5ecb8e807d2cb..22c286daca2bc 100644 --- a/stdlib/public/Backtracing/Context.swift +++ b/stdlib/public/Backtracing/Context.swift @@ -831,7 +831,7 @@ extension arm_gprs { // .. Darwin specifics ......................................................... -#if os(macOS) +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) private func mach_thread_get_state(_ thread: thread_t, _ flavor: CInt, _ result: inout T) -> kern_return_t { diff --git a/stdlib/public/SwiftShims/swift/shims/_SwiftBacktracing.h b/stdlib/public/SwiftShims/swift/shims/_SwiftBacktracing.h index 2d5ee2a5c6322..c993934a7341b 100644 --- a/stdlib/public/SwiftShims/swift/shims/_SwiftBacktracing.h +++ b/stdlib/public/SwiftShims/swift/shims/_SwiftBacktracing.h @@ -23,7 +23,7 @@ #include #endif -#if TARGET_OS_OSX +#if TARGET_OS_OSX || TARGET_OS_IPHONE #include #include @@ -76,7 +76,7 @@ struct arm_gprs { // .. Darwin specifics ......................................................... -#if TARGET_OS_OSX +#if TARGET_OS_OSX || TARGET_OS_IPHONE /* Darwin thread states. We can't import these from the system header because it uses all kinds of macros and the Swift importer can't cope with that. @@ -258,7 +258,7 @@ enum { kCRSanitizePathKeepFile = 0x100 << 1, }; -#endif // TARGET_OS_OSX +#endif // TARGET_OS_OSX || TARGET_OS_IPHONE #ifdef __cplusplus } // extern "C" From 27d907393a62d2a82ac3af2147780bf9a9c84a79 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Mon, 20 Feb 2023 11:26:24 +0000 Subject: [PATCH 10/22] [Backtracing][Runtime] Fix Linux build to use getauxval(AT_SECURE). Linux doesn't have `issetugid()`, so use `getauxval(AT_SECURE)` there instead. rdar://105391747 --- stdlib/public/runtime/Backtrace.cpp | 55 ++++++++++++++++++----------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/stdlib/public/runtime/Backtrace.cpp b/stdlib/public/runtime/Backtrace.cpp index 4a42d51d45412..2f5010a68cf84 100644 --- a/stdlib/public/runtime/Backtrace.cpp +++ b/stdlib/public/runtime/Backtrace.cpp @@ -27,6 +27,10 @@ #include "swift/Demangling/Demangler.h" +#ifdef __linux__ +#include +#endif + #ifdef _WIN32 #include #else @@ -212,17 +216,29 @@ const char *presetToString(Preset preset) { } #endif +#ifdef __linux__ +bool isPrivileged() { + return getauxval(AT_SECURE); +} +#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) +bool isPrivileged() { + return issetugid(); +} +#elif _WIN32 +bool isPrivileged() { + return false; +} +#endif + } // namespace BacktraceInitializer::BacktraceInitializer() { const char *backtracing = swift::runtime::environment::SWIFT_BACKTRACE(); -#if !_WIN32 // Force off for setuid processes. - if (issetugid()) { + if (isPrivileged()) { _swift_backtraceSettings.enabled = OnOffTty::Off; } -#endif if (backtracing) _swift_parseBacktracingSettings(backtracing); @@ -246,25 +262,22 @@ BacktraceInitializer::BacktraceInitializer() { _swift_backtraceSettings.enabled = OnOffTty::Off; } #else - #if !_WIN32 - if (issetugid()) { - if (_swift_backtraceSettings.enabled != OnOffTty::Off) { - // You'll only see this warning if you do e.g. - // - // SWIFT_BACKTRACE=enable=on /path/to/some/setuid/binary - // - // as opposed to - // - // /path/to/some/setuid/binary - // - // i.e. when you're trying to force matters. - swift::warning(0, - "swift runtime: backtrace-on-crash is not supported for " - "setuid executables.\n"); - _swift_backtraceSettings.enabled = OnOffTty::Off; - } + + if (isPrivileged() && _swift_backtraceSettings.enabled != OnOffTty::Off) { + // You'll only see this warning if you do e.g. + // + // SWIFT_BACKTRACE=enable=on /path/to/some/setuid/binary + // + // as opposed to + // + // /path/to/some/setuid/binary + // + // i.e. when you're trying to force matters. + swift::warning(0, + "swift runtime: backtrace-on-crash is not supported for " + "privileged executables.\n"); + _swift_backtraceSettings.enabled = OnOffTty::Off; } - #endif // !_WIN32 if (_swift_backtraceSettings.enabled == OnOffTty::TTY) _swift_backtraceSettings.enabled = From 43794157f24e0e2dcb9f9a6b556a22552faa91be Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Tue, 28 Feb 2023 11:28:04 +0000 Subject: [PATCH 11/22] [Backtracing][Runtime] Fix a copy/paste blunder. Accidentally wrote `thread_resume()` instead of `thread_suspend()`. rdar://105391747 --- stdlib/public/runtime/CrashHandlerMacOS.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stdlib/public/runtime/CrashHandlerMacOS.cpp b/stdlib/public/runtime/CrashHandlerMacOS.cpp index 382d59d6eb773..17a2e0919143f 100644 --- a/stdlib/public/runtime/CrashHandlerMacOS.cpp +++ b/stdlib/public/runtime/CrashHandlerMacOS.cpp @@ -153,7 +153,7 @@ suspend_other_threads() continue; // Ignore the results of these two; if they fail there's nothing we can do - (void)thread_resume(threads[n]); + (void)thread_suspend(threads[n]); (void)mach_port_deallocate(mach_task_self(), threads[n]); } From 4169b4e59f9666dbdb7868e0e5da3de8756286c5 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Mon, 20 Feb 2023 16:31:39 +0000 Subject: [PATCH 12/22] [Backtracing] Various improvements following Johannes' remarks. Removed some unnecessary memory rebinding. Made `CFString` conversion slightly more efficient. Provide the `SharedCacheInfo` fields everywhere, but make it optional all over as well. rdar://104336548 --- stdlib/public/Backtracing/Backtrace.swift | 17 +++++-------- .../Backtracing/BacktraceFormatter.swift | 2 +- .../Backtracing/CoreSymbolication.swift | 10 ++++---- .../Backtracing/SymbolicatedBacktrace.swift | 24 +++++++++---------- 4 files changed, 23 insertions(+), 30 deletions(-) diff --git a/stdlib/public/Backtracing/Backtrace.swift b/stdlib/public/Backtracing/Backtrace.swift index dbeaeb10057c0..0742b73239c02 100644 --- a/stdlib/public/Backtracing/Backtrace.swift +++ b/stdlib/public/Backtracing/Backtrace.swift @@ -180,7 +180,6 @@ public struct Backtrace: CustomStringConvertible, Sendable { /// Holds information about the shared cache. public struct SharedCacheInfo: Sendable { - #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) /// The UUID from the shared cache. public var uuid: [UInt8] @@ -189,7 +188,6 @@ public struct Backtrace: CustomStringConvertible, Sendable { /// Says whether there is in fact a shared cache. public var noCache: Bool - #endif } /// Information about the shared cache. @@ -401,34 +399,31 @@ public struct Backtrace: CustomStringConvertible, Sendable { /// Capture shared cache information. /// /// @returns A `SharedCacheInfo`. - public static func captureSharedCacheInfo() -> SharedCacheInfo { + public static func captureSharedCacheInfo() -> SharedCacheInfo? { #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) return captureSharedCacheInfo(for: mach_task_self_) #else - return SharedCacheInfo() + return nil #endif } @_spi(Internal) - public static func captureSharedCacheInfo(for t: Any) -> SharedCacheInfo { + public static func captureSharedCacheInfo(for t: Any) -> SharedCacheInfo? { #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) let task = t as! task_t return withDyldProcessInfo(for: task){ dyldInfo in var cacheInfo = dyld_process_cache_info() _dyld_process_info_get_cache(dyldInfo, &cacheInfo) let theUUID = withUnsafePointer(to: cacheInfo.cacheUUID){ - $0.withMemoryRebound(to: UInt8.self, - capacity: MemoryLayout.size) { - Array(UnsafeBufferPointer(start: $0, - count: MemoryLayout.size)) - } + Array(UnsafeRawBufferPointer(start: $0, + count: MemoryLayout.size)) } return SharedCacheInfo(uuid: theUUID, baseAddress: cacheInfo.cacheBaseAddress, noCache: cacheInfo.noCache) } #else // !os(Darwin) - return SharedCacheInfo() + return nil #endif } diff --git a/stdlib/public/Backtracing/BacktraceFormatter.swift b/stdlib/public/Backtracing/BacktraceFormatter.swift index 6407da9630cf8..72ee169fa3212 100644 --- a/stdlib/public/Backtracing/BacktraceFormatter.swift +++ b/stdlib/public/Backtracing/BacktraceFormatter.swift @@ -690,7 +690,7 @@ public struct BacktraceFormatter { while feof(fp) == 0 && ferror(fp) == 0 { guard let result = fgets(buffer.baseAddress, - Int32(buffer.count), fp) else { + CInt(buffer.count), fp) else { break } diff --git a/stdlib/public/Backtracing/CoreSymbolication.swift b/stdlib/public/Backtracing/CoreSymbolication.swift index 9ae631cc69681..bd5911f1374cb 100644 --- a/stdlib/public/Backtracing/CoreSymbolication.swift +++ b/stdlib/public/Backtracing/CoreSymbolication.swift @@ -117,8 +117,8 @@ private enum Sym { // CFString. As a result, we need to do the dance manually. private func toCFString(_ s: String) -> CFString! { - let bytes = Array(s.utf8) - return bytes.withUnsafeBufferPointer{ + var s = s + return s.withUTF8 { return CFStringCreateWithBytes(nil, $0.baseAddress, $0.count, @@ -135,10 +135,8 @@ private func fromCFString(_ cf: CFString) -> String { if let ptr = CFStringGetCStringPtr(cf, CFStringBuiltInEncodings.ASCII.rawValue) { - return ptr.withMemoryRebound(to: UInt8.self, capacity: length) { - return String(decoding: UnsafeBufferPointer(start: $0, count: length), - as: UTF8.self) - } + return String(decoding: UnsafeRawBufferPointer(start: ptr, count: length), + as: UTF8.self) } else { var byteLen = CFIndex(0) diff --git a/stdlib/public/Backtracing/SymbolicatedBacktrace.swift b/stdlib/public/Backtracing/SymbolicatedBacktrace.swift index 50886c5e129d7..af57dbc425ac7 100644 --- a/stdlib/public/Backtracing/SymbolicatedBacktrace.swift +++ b/stdlib/public/Backtracing/SymbolicatedBacktrace.swift @@ -211,7 +211,7 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { public var images: [Backtrace.Image] /// Shared cache information. - public var sharedCacheInfo: Backtrace.SharedCacheInfo + public var sharedCacheInfo: Backtrace.SharedCacheInfo? /// True if this backtrace is a Swift runtime failure. public var isSwiftRuntimeFailure: Bool { @@ -233,7 +233,7 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { /// Construct a SymbolicatedBacktrace from a backtrace and a list of images. private init(backtrace: Backtrace, images: [Backtrace.Image], - sharedCacheInfo: Backtrace.SharedCacheInfo, + sharedCacheInfo: Backtrace.SharedCacheInfo?, frames: [Frame]) { self.backtrace = backtrace self.images = images @@ -258,7 +258,7 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { /// Create a symbolicator. private static func withSymbolicator(images: [Backtrace.Image], - sharedCacheInfo: Backtrace.SharedCacheInfo, + sharedCacheInfo: Backtrace.SharedCacheInfo?, fn: (CSSymbolicatorRef) throws -> T) rethrows -> T { let binaryImageList = images.map{ image in BinaryImageInformation( @@ -357,7 +357,7 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { theImages = Backtrace.captureImages() } - let theCacheInfo: Backtrace.SharedCacheInfo + let theCacheInfo: Backtrace.SharedCacheInfo? if let sharedCacheInfo = sharedCacheInfo { theCacheInfo = sharedCacheInfo } else if let sharedCacheInfo = backtrace.sharedCacheInfo { @@ -450,14 +450,14 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { lines.append("\(n)\t\(image)") } - #if os(macOS) || os(iOS) || os(tvOS) || os(watchOS) - lines.append("") - lines.append("Shared Cache:") - lines.append("") - lines.append(" UUID: \(hex(sharedCacheInfo.uuid))") - lines.append(" Base: \(hex(sharedCacheInfo.baseAddress))") - lines.append(" Active: \(!sharedCacheInfo.noCache)") - #endif + if let sharedCacheInfo = sharedCacheInfo { + lines.append("") + lines.append("Shared Cache:") + lines.append("") + lines.append(" UUID: \(hex(sharedCacheInfo.uuid))") + lines.append(" Base: \(hex(sharedCacheInfo.baseAddress))") + lines.append(" Active: \(!sharedCacheInfo.noCache)") + } return lines.joined(separator: "\n") } From 9fb5b874f2b5f9b5e92d127b2951431e27238a14 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Tue, 28 Feb 2023 12:46:17 +0000 Subject: [PATCH 13/22] [Backtracing] Tweaks after Mike's remarks. Just use `UInt` for `Address`. This is still the subject of some discussion on the forums, but I haven't decided precisely what to do about it and `UInt` makes sense for now. This also necessitated some casts elsewhere. Improve some comments. Made `limit` robust against silly negative values. A couple of formatting fixes. Don't bother supporting the macOS 10.12.2 SDK as Xcode now supports a minimum of 10.13. --- stdlib/public/Backtracing/Backtrace.swift | 39 ++++++++++--------- .../Backtracing/BacktraceFormatter.swift | 6 +-- .../Backtracing/SymbolicatedBacktrace.swift | 14 +++---- 3 files changed, 29 insertions(+), 30 deletions(-) diff --git a/stdlib/public/Backtracing/Backtrace.swift b/stdlib/public/Backtracing/Backtrace.swift index 0742b73239c02..4eb11d7e5daaa 100644 --- a/stdlib/public/Backtracing/Backtrace.swift +++ b/stdlib/public/Backtracing/Backtrace.swift @@ -27,14 +27,10 @@ import Swift public struct Backtrace: CustomStringConvertible, Sendable { /// The type of an address. /// - /// This will be `UInt32` or `UInt64` on current platforms. - #if arch(x86_64) || arch(arm64) - public typealias Address = UInt64 - #elseif arch(i386) || arch(arm) - public typealias Address = UInt32 - #else - #error("You need to fill this in for your architecture.") - #endif + /// This is intentionally _not_ a pointer, because you shouldn't be + /// dereferencing them; they may refer to some other process, for + /// example. + public typealias Address = UInt /// The unwind algorithm to use. public enum UnwindAlgorithm { @@ -55,20 +51,27 @@ public struct Backtrace: CustomStringConvertible, Sendable { /// Represents an individual frame in a backtrace. public enum Frame: CustomStringConvertible, Sendable { - /// An accurate program counter. + /// A program counter value. /// /// This might come from a signal handler, or an exception or some /// other situation in which we have captured the actual program counter. + /// + /// These can be directly symbolicated, as-is, with no adjustment. case programCounter(Address) /// A return address. /// /// Corresponds to a normal function call. + /// + /// Requires adjustment when symbolicating for a backtrace, because it + /// points at the address after the one that triggered the child frame. case returnAddress(Address) /// An async resume point. /// /// Corresponds to an `await` in an async task. + /// + /// Can be directly symbolicated, as-is. case asyncResumePoint(Address) /// Indicates a discontinuity in the backtrace. @@ -174,7 +177,7 @@ public struct Backtrace: CustomStringConvertible, Sendable { /// /// Some backtracing algorithms may require this information, in which case /// it will be filled in by the `capture()` method. Other algorithms may - /// not, in which case it will be empty and you can capture an image list + /// not, in which case it will be `nil` and you can capture an image list /// separately yourself using `captureImages()`. public var images: [Image]? @@ -249,7 +252,7 @@ public struct Backtrace: CustomStringConvertible, Sendable { .dropFirst(offset) if let limit = limit { - if limit == 0 { + if limit <= 0 { return Backtrace(frames: [.truncated]) } @@ -356,7 +359,7 @@ public struct Backtrace: CustomStringConvertible, Sendable { #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) let task = process as! task_t - withDyldProcessInfo(for: task){ dyldInfo in + withDyldProcessInfo(for: task) { dyldInfo in _dyld_process_info_for_each_image(dyldInfo) { (machHeaderAddress, uuid, path) in @@ -375,7 +378,7 @@ public struct Backtrace: CustomStringConvertible, Sendable { // Find the end of the __TEXT segment var endOfText = machHeaderAddress + 4096 - _dyld_process_info_for_each_segment(dyldInfo, machHeaderAddress){ + _dyld_process_info_for_each_segment(dyldInfo, machHeaderAddress) { address, size, name in if let name = String(validatingUTF8: name!), name == "__TEXT" { @@ -386,8 +389,8 @@ public struct Backtrace: CustomStringConvertible, Sendable { images.append(Image(name: name, path: pathString, buildID: theUUID, - baseAddress: machHeaderAddress, - endOfText: endOfText)) + baseAddress: Address(machHeaderAddress), + endOfText: Address(endOfText))) } } } @@ -411,15 +414,15 @@ public struct Backtrace: CustomStringConvertible, Sendable { public static func captureSharedCacheInfo(for t: Any) -> SharedCacheInfo? { #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) let task = t as! task_t - return withDyldProcessInfo(for: task){ dyldInfo in + return withDyldProcessInfo(for: task) { dyldInfo in var cacheInfo = dyld_process_cache_info() _dyld_process_info_get_cache(dyldInfo, &cacheInfo) - let theUUID = withUnsafePointer(to: cacheInfo.cacheUUID){ + let theUUID = withUnsafePointer(to: cacheInfo.cacheUUID) { Array(UnsafeRawBufferPointer(start: $0, count: MemoryLayout.size)) } return SharedCacheInfo(uuid: theUUID, - baseAddress: cacheInfo.cacheBaseAddress, + baseAddress: Address(cacheInfo.cacheBaseAddress), noCache: cacheInfo.noCache) } #else // !os(Darwin) diff --git a/stdlib/public/Backtracing/BacktraceFormatter.swift b/stdlib/public/Backtracing/BacktraceFormatter.swift index 72ee169fa3212..bf4be723d44f3 100644 --- a/stdlib/public/Backtracing/BacktraceFormatter.swift +++ b/stdlib/public/Backtracing/BacktraceFormatter.swift @@ -294,11 +294,7 @@ private func measure(_ ch: Unicode.Scalar) -> Int { return 1 } - if #available(macOS 10.12.2, *) { - if ch.properties.isEmoji { - return 2 - } - } else if ch.value >= 0x1f100 && ch.value <= 0x1fb00 { + if ch.properties.isEmoji { return 2 } diff --git a/stdlib/public/Backtracing/SymbolicatedBacktrace.swift b/stdlib/public/Backtracing/SymbolicatedBacktrace.swift index af57dbc425ac7..0c035c9f8bc8f 100644 --- a/stdlib/public/Backtracing/SymbolicatedBacktrace.swift +++ b/stdlib/public/Backtracing/SymbolicatedBacktrace.swift @@ -262,15 +262,15 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { fn: (CSSymbolicatorRef) throws -> T) rethrows -> T { let binaryImageList = images.map{ image in BinaryImageInformation( - base: image.baseAddress, - extent: image.endOfText, + base: mach_vm_address_t(image.baseAddress), + extent: mach_vm_address_t(image.endOfText), uuid: uuidBytesFromBuildID(image.buildID!), arch: HostContext.coreSymbolicationArchitecture, path: image.path, relocations: [ BinaryRelocationInformation( - base: image.baseAddress, - extent: image.endOfText, + base: mach_vm_address_t(image.baseAddress), + extent: mach_vm_address_t(image.endOfText), name: "__TEXT" ) ], @@ -333,7 +333,7 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { let theSymbol = Symbol(imageIndex: imageIndex, imageName: imageName, rawName: rawName, - offset: Int(address - range.location), + offset: Int(address - UInt(range.location)), sourceLocation: location) theSymbol.name = name @@ -376,7 +376,7 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { case .omittedFrames(_), .truncated: frames.append(Frame(captured: frame, symbol: nil)) default: - let address = frame.adjustedProgramCounter + let address = mach_vm_address_t(frame.adjustedProgramCounter) let owner = CSSymbolicatorGetSymbolOwnerWithAddressAtTime(symbolicator, address, @@ -390,7 +390,7 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { let pos = frames.count var first = true - _ = CSSymbolOwnerForEachStackFrameAtAddress(owner, address){ + _ = CSSymbolOwnerForEachStackFrameAtAddress(owner, address) { symbol, sourceInfo in frames.insert(buildFrame(from: frame, From 10b3dd4b2727c7068c6e1cf9c57d19e6c3915ee6 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Fri, 3 Mar 2023 11:06:52 +0000 Subject: [PATCH 14/22] [Backtracing] Don't try to include for iOS. iOS doesn't have . We don't need it anyway there. rdar://104336548 --- stdlib/public/SwiftShims/swift/shims/_SwiftBacktracing.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/stdlib/public/SwiftShims/swift/shims/_SwiftBacktracing.h b/stdlib/public/SwiftShims/swift/shims/_SwiftBacktracing.h index c993934a7341b..5f96519fdfb3b 100644 --- a/stdlib/public/SwiftShims/swift/shims/_SwiftBacktracing.h +++ b/stdlib/public/SwiftShims/swift/shims/_SwiftBacktracing.h @@ -29,7 +29,9 @@ #include #include +#endif +#if TARGET_OS_OSX #include #endif From 5c69bb5e8685c07ee9ba15bfce10d8b7121eb0fa Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Fri, 3 Mar 2023 11:22:50 +0000 Subject: [PATCH 15/22] [Backtracing] Only include libproc.h if it's present. rdar://104336548 --- .../SwiftShims/swift/shims/_SwiftBacktracing.h | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/stdlib/public/SwiftShims/swift/shims/_SwiftBacktracing.h b/stdlib/public/SwiftShims/swift/shims/_SwiftBacktracing.h index 5f96519fdfb3b..0dea31890b67c 100644 --- a/stdlib/public/SwiftShims/swift/shims/_SwiftBacktracing.h +++ b/stdlib/public/SwiftShims/swift/shims/_SwiftBacktracing.h @@ -29,10 +29,19 @@ #include #include -#endif -#if TARGET_OS_OSX +#if __has_include() #include +#else +#ifdef __cplusplus +extern "C" { +#endif +extern int proc_name(int pid, void * buffer, uint32_t buffersize); +#ifdef __cplusplus +} +#endif +#endif + #endif #ifdef __cplusplus From e75ec9cc7d29381a97df4ff5c086cfe07ddd95a6 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Mon, 13 Feb 2023 15:30:32 +0000 Subject: [PATCH 16/22] [Frontend] Add support for implicit import of _Backtracing Once the API has gone through Swift Evolution, we will want to implicitly import the _Backtracing module. Add code to do that, but set it to off by default for now. rdar://105394140 --- CMakeLists.txt | 4 ++ cmake/modules/SwiftImplicitImport.cmake | 20 +++++++++ include/swift/AST/DiagnosticsFrontend.def | 2 + include/swift/AST/KnownIdentifiers.def | 3 ++ include/swift/Basic/LangOptions.h | 4 ++ include/swift/Config.h.in | 2 + include/swift/Frontend/Frontend.h | 12 ++++++ include/swift/Option/FrontendOptions.td | 8 ++++ include/swift/Strings.h | 2 + lib/AST/NameLookup.cpp | 16 +++++++ lib/ASTGen/CMakeLists.txt | 25 +++++------ lib/DriverTool/autolink_extract_main.cpp | 3 +- lib/Frontend/CompilerInvocation.cpp | 5 +++ lib/Frontend/Frontend.cpp | 42 +++++++++++++++++++ stdlib/cmake/modules/AddSwiftStdlib.cmake | 7 ++-- test/Backtracing/NotImportedByDefault.swift | 6 +++ test/Backtracing/SimpleBacktrace.swift | 4 +- .../tools/sourcekitd-test/Options.td | 8 ++++ .../tools/sourcekitd-test/TestOptions.cpp | 4 ++ .../tools/sourcekitd-test/TestOptions.h | 2 + .../tools/sourcekitd-test/sourcekitd-test.cpp | 14 +++++++ tools/swift-ide-test/swift-ide-test.cpp | 18 ++++++++ 22 files changed, 191 insertions(+), 20 deletions(-) create mode 100644 cmake/modules/SwiftImplicitImport.cmake create mode 100644 test/Backtracing/NotImportedByDefault.swift diff --git a/CMakeLists.txt b/CMakeLists.txt index 49cf0395cd83d..03acd88288b41 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -570,6 +570,10 @@ option(SWIFT_IMPLICIT_CONCURRENCY_IMPORT "Implicitly import the Swift concurrency module" TRUE) +option(SWIFT_IMPLICIT_BACKTRACING_IMPORT + "Implicitly import the Swift backtracing module" + FALSE) + option(SWIFT_ENABLE_EXPERIMENTAL_CONCURRENCY "Enable build of the Swift concurrency module" FALSE) diff --git a/cmake/modules/SwiftImplicitImport.cmake b/cmake/modules/SwiftImplicitImport.cmake new file mode 100644 index 0000000000000..1dbc8bcecb2dc --- /dev/null +++ b/cmake/modules/SwiftImplicitImport.cmake @@ -0,0 +1,20 @@ +# Test if the Swift compiler supports -disable-implicit--module-import +function(swift_supports_implicit_module module_name out_var) + file(WRITE "${CMAKE_BINARY_DIR}/tmp/empty-check-${module_name}.swift" "") + execute_process( + COMMAND + "${CMAKE_Swift_COMPILER}" + -Xfrontend -disable-implicit-${module_name}-module-import + -c - -o /dev/null + INPUT_FILE + "${CMAKE_BINARY_DIR}/tmp/empty-check-${module_name}.swift" + OUTPUT_QUIET ERROR_QUIET + RESULT_VARIABLE + result + ) + if(NOT result) + set("${out_var}" "TRUE" PARENT_SCOPE) + else() + set("${out_var}" "FALSE" PARENT_SCOPE) + endif() +endfunction() diff --git a/include/swift/AST/DiagnosticsFrontend.def b/include/swift/AST/DiagnosticsFrontend.def index 23ed5867c8f22..744d0d3177e80 100644 --- a/include/swift/AST/DiagnosticsFrontend.def +++ b/include/swift/AST/DiagnosticsFrontend.def @@ -172,6 +172,8 @@ WARNING(warn_implicit_concurrency_import_failed,none, "unable to perform implicit import of \"_Concurrency\" module: no such module found", ()) REMARK(warn_implicit_string_processing_import_failed,none, "unable to perform implicit import of \"_StringProcessing\" module: no such module found", ()) +REMARK(warn_implicit_backtracing_import_failed,none, + "unable to perform implicit import of \"_Backtracing\" module: no such module found", ()) ERROR(error_module_name_required,none, "-module-name is required", ()) ERROR(error_bad_module_name,none, diff --git a/include/swift/AST/KnownIdentifiers.def b/include/swift/AST/KnownIdentifiers.def index 57cbab1f95226..d91b4b6a4a9a9 100644 --- a/include/swift/AST/KnownIdentifiers.def +++ b/include/swift/AST/KnownIdentifiers.def @@ -263,6 +263,9 @@ IDENTIFIER(version) IDENTIFIER_(StringProcessing) IDENTIFIER(RegexBuilder) +// Backtracing +IDENTIFIER_(Backtracing) + // Distributed actors IDENTIFIER(ActorID) IDENTIFIER(ActorSystem) diff --git a/include/swift/Basic/LangOptions.h b/include/swift/Basic/LangOptions.h index 6ac672ca64a0e..b0fba00fe9711 100644 --- a/include/swift/Basic/LangOptions.h +++ b/include/swift/Basic/LangOptions.h @@ -352,6 +352,10 @@ namespace swift { /// Disable the implicit import of the _StringProcessing module. bool DisableImplicitStringProcessingModuleImport = false; + /// Disable the implicit import of the _Backtracing module. + bool DisableImplicitBacktracingModuleImport = + !SWIFT_IMPLICIT_BACKTRACING_IMPORT; + /// Should we check the target OSs of serialized modules to see that they're /// new enough? bool EnableTargetOSChecking = true; diff --git a/include/swift/Config.h.in b/include/swift/Config.h.in index 495c34d1fefb7..3e26daaaec5f7 100644 --- a/include/swift/Config.h.in +++ b/include/swift/Config.h.in @@ -10,6 +10,8 @@ #cmakedefine01 SWIFT_IMPLICIT_CONCURRENCY_IMPORT +#cmakedefine01 SWIFT_IMPLICIT_BACKTRACING_IMPORT + #cmakedefine01 SWIFT_ENABLE_EXPERIMENTAL_DISTRIBUTED #cmakedefine01 SWIFT_ENABLE_GLOBAL_ISEL_ARM64 diff --git a/include/swift/Frontend/Frontend.h b/include/swift/Frontend/Frontend.h index 4ea8e4c963a40..30785440fa5fe 100644 --- a/include/swift/Frontend/Frontend.h +++ b/include/swift/Frontend/Frontend.h @@ -380,6 +380,10 @@ class CompilerInvocation { /// imported. bool shouldImportSwiftStringProcessing() const; + /// Whether the Swift Backtracing support library should be implicitly + /// imported. + bool shouldImportSwiftBacktracing() const; + /// Performs input setup common to these tools: /// sil-opt, sil-func-extractor, sil-llvm-gen, and sil-nm. /// Return value includes the buffer so caller can keep it alive. @@ -575,6 +579,14 @@ class CompilerInstance { /// i.e. if it can be found. bool canImportSwiftStringProcessing() const; + /// Verify that if an implicit import of the `Backtracing` module if + /// expected, it can actually be imported. Emit a warning, otherwise. + void verifyImplicitBacktracingImport(); + + /// Whether the Swift Backtracing support library can be imported + /// i.e. if it can be found. + bool canImportSwiftBacktracing() const; + /// Whether the CxxShim library can be imported /// i.e. if it can be found. bool canImportCxxShim() const; diff --git a/include/swift/Option/FrontendOptions.td b/include/swift/Option/FrontendOptions.td index 3b98cd4dd0c0d..0e942767a3867 100644 --- a/include/swift/Option/FrontendOptions.td +++ b/include/swift/Option/FrontendOptions.td @@ -441,6 +441,14 @@ def disable_implicit_string_processing_module_import : Flag<["-"], "disable-implicit-string-processing-module-import">, HelpText<"Disable the implicit import of the _StringProcessing module.">; +def enable_implicit_backtracing_module_import : Flag<["-"], + "enable-implicit-backtracing-module-import">, + HelpText<"Enable the implicit import of the _Backtracing module.">; + +def disable_implicit_backtracing_module_import : Flag<["-"], + "disable-implicit-backtracing-module-import">, + HelpText<"Disable the implicit import of the _Backtracing module.">; + def disable_arc_opts : Flag<["-"], "disable-arc-opts">, HelpText<"Don't run SIL ARC optimization passes.">; def disable_ossa_opts : Flag<["-"], "disable-ossa-opts">, diff --git a/include/swift/Strings.h b/include/swift/Strings.h index 745e2c3e2def8..ddb8d4fb15796 100644 --- a/include/swift/Strings.h +++ b/include/swift/Strings.h @@ -30,6 +30,8 @@ constexpr static const StringLiteral SWIFT_CONCURRENCY_SHIMS_NAME = "_SwiftConcu constexpr static const StringLiteral SWIFT_DISTRIBUTED_NAME = "Distributed"; /// The name of the StringProcessing module, which supports that extension. constexpr static const StringLiteral SWIFT_STRING_PROCESSING_NAME = "_StringProcessing"; +/// The name of the Backtracing module, which supports that extension. +constexpr static const StringLiteral SWIFT_BACKTRACING_NAME = "_Backtracing"; /// The name of the SwiftShims module, which contains private stdlib decls. constexpr static const StringLiteral SWIFT_SHIMS_NAME = "SwiftShims"; /// The name of the CxxShim module, which contains a cxx casting utility. diff --git a/lib/AST/NameLookup.cpp b/lib/AST/NameLookup.cpp index 768f77dcacae9..f2d95dd4a5b14 100644 --- a/lib/AST/NameLookup.cpp +++ b/lib/AST/NameLookup.cpp @@ -662,6 +662,22 @@ static void recordShadowedDeclsAfterTypeMatch( } } + // Next, prefer any other module over the _Backtracing module. + if (auto spModule = ctx.getLoadedModule(ctx.Id_Backtracing)) { + if ((firstModule == spModule) != (secondModule == spModule)) { + // If second module is _StringProcessing, then it is shadowed by + // first. + if (secondModule == spModule) { + shadowed.insert(secondDecl); + continue; + } + + // Otherwise, the first declaration is shadowed by the second. + shadowed.insert(firstDecl); + break; + } + } + // The Foundation overlay introduced Data.withUnsafeBytes, which is // treated as being ambiguous with SwiftNIO's Data.withUnsafeBytes // extension. Apply a special-case name shadowing rule to use the diff --git a/lib/ASTGen/CMakeLists.txt b/lib/ASTGen/CMakeLists.txt index ebca5d6616e51..8c25baf91f41f 100644 --- a/lib/ASTGen/CMakeLists.txt +++ b/lib/ASTGen/CMakeLists.txt @@ -1,23 +1,24 @@ +include(SwiftImplicitImport) + if (SWIFT_SWIFT_PARSER) # Ensure that we do not link the _StringProcessing module. But we can # only pass this flag for new-enough compilers that support it. - file(WRITE "${CMAKE_BINARY_DIR}/tmp/empty-check-string-processing.swift" "") - execute_process( - COMMAND - "${CMAKE_Swift_COMPILER}" - -Xfrontend -disable-implicit-string-processing-module-import - -c - -o /dev/null - INPUT_FILE - "${CMAKE_BINARY_DIR}/tmp/empty-check-string-processing.swift" - OUTPUT_QUIET ERROR_QUIET - RESULT_VARIABLE - SWIFT_SUPPORTS_DISABLE_IMPLICIT_STRING_PROCESSING_MODULE_IMPORT) - if (NOT SWIFT_SUPPORTS_DISABLE_IMPLICIT_STRING_PROCESSING_MODULE_IMPORT) + swift_supports_implicit_module("string-processing" + SWIFT_SUPPORTS_DISABLE_IMPLICIT_STRING_PROCESSING_MODULE_IMPORT) + if (SWIFT_SUPPORTS_DISABLE_IMPLICIT_STRING_PROCESSING_MODULE_IMPORT) add_compile_options( $<$:-Xfrontend> $<$:-disable-implicit-string-processing-module-import>) endif() + swift_supports_implicit_module("backtracing" + SWIFT_SUPPORTS_DISABLE_IMPLICIT_BACKTRACING_MODULE_IMPORT) + if (SWIFT_SUPPORTS_DISABLE_IMPLICIT_BACKTRACING_MODULE_IMPORT) + add_compile_options( + $<$:-Xfrontend> + $<$:-disable-implicit-backtracing-module-import>) + endif() + add_library(swiftASTGen STATIC Sources/ASTGen/ASTGen.swift Sources/ASTGen/Decls.swift diff --git a/lib/DriverTool/autolink_extract_main.cpp b/lib/DriverTool/autolink_extract_main.cpp index 3d6b7fdb8a2e1..efcbbb1e096ab 100644 --- a/lib/DriverTool/autolink_extract_main.cpp +++ b/lib/DriverTool/autolink_extract_main.cpp @@ -250,7 +250,8 @@ int autolink_extract_main(ArrayRef Args, const char *Argv0, {"-lswiftCore", false}, {"-lswift_Concurrency", false}, {"-lswift_StringProcessing", false}, - {"-lswift_RegexParser", false} + {"-lswift_RegexParser", false}, + {"-lswift_Backtracing", false}, }; // Extract the linker flags from the objects. diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index ab4d201d876fa..eb24018ea6053 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -487,6 +487,11 @@ static bool ParseLangArgs(LangOptions &Opts, ArgList &Args, Opts.DisableImplicitStringProcessingModuleImport |= Args.hasArg(OPT_disable_implicit_string_processing_module_import); + Opts.DisableImplicitBacktracingModuleImport = + Args.hasFlag(OPT_disable_implicit_backtracing_module_import, + OPT_enable_implicit_backtracing_module_import, + true); + if (Args.hasArg(OPT_enable_experimental_async_top_level)) Diags.diagnose(SourceLoc(), diag::warn_flag_deprecated, "-enable-experimental-async-top-level"); diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index 6a26beff5e30b..4575629a35eda 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -848,6 +848,20 @@ bool CompilerInvocation::shouldImportSwiftStringProcessing() const { FrontendOptions::ParseInputMode::SwiftModuleInterface; } +/// Enable Swift backtracing on a per-target basis +static bool shouldImportSwiftBacktracingByDefault(const llvm::Triple &target) { + if (target.isOSDarwin() || target.isOSWindows() || target.isOSLinux()) + return true; + return false; +} + +bool CompilerInvocation::shouldImportSwiftBacktracing() const { + return shouldImportSwiftBacktracingByDefault(getLangOptions().Target) && + !getLangOptions().DisableImplicitBacktracingModuleImport && + getFrontendOptions().InputMode != + FrontendOptions::ParseInputMode::SwiftModuleInterface; +} + /// Implicitly import the SwiftOnoneSupport module in non-optimized /// builds. This allows for use of popular specialized functions /// from the standard library, which makes the non-optimized builds @@ -912,6 +926,21 @@ bool CompilerInstance::canImportSwiftStringProcessing() const { return getASTContext().canImportModule(modulePath); } +void CompilerInstance::verifyImplicitBacktracingImport() { + if (Invocation.shouldImportSwiftBacktracing() && + !canImportSwiftBacktracing()) { + Diagnostics.diagnose(SourceLoc(), + diag::warn_implicit_backtracing_import_failed); + } +} + +bool CompilerInstance::canImportSwiftBacktracing() const { + ImportPath::Module::Builder builder( + getASTContext().getIdentifier(SWIFT_BACKTRACING_NAME)); + auto modulePath = builder.get(); + return getASTContext().canImportModule(modulePath); +} + bool CompilerInstance::canImportCxxShim() const { ImportPath::Module::Builder builder( getASTContext().getIdentifier(CXX_SHIM_NAME)); @@ -976,6 +1005,19 @@ ImplicitImportInfo CompilerInstance::getImplicitImportInfo() const { } } + if (Invocation.shouldImportSwiftBacktracing()) { + switch (imports.StdlibKind) { + case ImplicitStdlibKind::Builtin: + case ImplicitStdlibKind::None: + break; + + case ImplicitStdlibKind::Stdlib: + if (canImportSwiftBacktracing()) + pushImport(SWIFT_BACKTRACING_NAME); + break; + } + } + if (Invocation.getLangOptions().EnableCXXInterop && canImportCxxShim()) { pushImport(CXX_SHIM_NAME); } diff --git a/stdlib/cmake/modules/AddSwiftStdlib.cmake b/stdlib/cmake/modules/AddSwiftStdlib.cmake index 22f596afe8192..b900581bb39dc 100644 --- a/stdlib/cmake/modules/AddSwiftStdlib.cmake +++ b/stdlib/cmake/modules/AddSwiftStdlib.cmake @@ -1824,10 +1824,9 @@ function(add_swift_target_library name) "-Xfrontend;-disable-implicit-string-processing-module-import") endif() - # Turn off implicit import of _StringProcessing when building libraries - if(SWIFT_ENABLE_EXPERIMENTAL_STRING_PROCESSING) - list(APPEND SWIFTLIB_SWIFT_COMPILE_FLAGS - "-Xfrontend;-disable-implicit-string-processing-module-import") + # Turn off implicit import of _Backtracing when building libraries + if(SWIFT_IMPLICIT_BACKTRACING_IMPORT) + list(APPEND SWIFTLIB_SWIFT_COMPILE_FLAGS "-Xfrontend;-disable-implicit-backtracing-module-import") endif() if(SWIFTLIB_IS_STDLIB AND SWIFT_STDLIB_ENABLE_PRESPECIALIZATION) diff --git a/test/Backtracing/NotImportedByDefault.swift b/test/Backtracing/NotImportedByDefault.swift new file mode 100644 index 0000000000000..22ab0fcfd0367 --- /dev/null +++ b/test/Backtracing/NotImportedByDefault.swift @@ -0,0 +1,6 @@ +// RUN: %empty-directory(%t) +// RUN: ( %target-build-swift %s -o %t/NotImportedByDefault || true) 2>&1 | %FileCheck %s + +let backtrace = try! Backtrace.capture() + +// CHECK: error: cannot find 'Backtrace' in scope diff --git a/test/Backtracing/SimpleBacktrace.swift b/test/Backtracing/SimpleBacktrace.swift index 1612fa9088e7d..2bf14b4188b2a 100644 --- a/test/Backtracing/SimpleBacktrace.swift +++ b/test/Backtracing/SimpleBacktrace.swift @@ -1,13 +1,11 @@ // RUN: %empty-directory(%t) -// RUN: %target-build-swift %s -Xfrontend -parse-as-library -Onone -o %t/SimpleBacktrace +// RUN: %target-build-swift %s -Xfrontend -enable-implicit-backtracing-module-import -parse-as-library -Onone -o %t/SimpleBacktrace // RUN: %target-codesign %t/SimpleBacktrace // RUN: %target-run %t/SimpleBacktrace | %FileCheck %s // REQUIRES: executable_test // REQUIRES: OS=macosx || OS=iOS || OS=watchOS || OS=tvOS -import _Backtracing - func level1() { level2() } diff --git a/tools/SourceKit/tools/sourcekitd-test/Options.td b/tools/SourceKit/tools/sourcekitd-test/Options.td index a1168c38029c5..32bca04bff954 100644 --- a/tools/SourceKit/tools/sourcekitd-test/Options.td +++ b/tools/SourceKit/tools/sourcekitd-test/Options.td @@ -116,6 +116,14 @@ def disable_implicit_string_processing_module_import : Flag<["-"], "disable-implicit-string-processing-module-import">, HelpText<"Disable implicit import of the _StringProcessing module">; +def enable_implicit_backtracing_module_import : Flag<["-"], + "enable-implicit-backtracing-module-import">, + HelpText<"Enable implicit import of the _Backtracing module">; + +def disable_implicit_backtracing_module_import : Flag<["-"], + "disable-implicit-backtracing-module-import">, + HelpText<"Disable implicit import of the _Backtracing module">; + def end_pos : Separate<["-"], "end-pos">, HelpText<"line:col">; def end_pos_EQ : Joined<["-"], "end-pos=">, Alias; diff --git a/tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp b/tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp index 347a25c2067a8..809dd3e47bde9 100644 --- a/tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp +++ b/tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp @@ -455,6 +455,10 @@ bool TestOptions::parseArgs(llvm::ArrayRef Args) { DisableImplicitStringProcessingModuleImport = true; break; + case OPT_disable_implicit_backtracing_module_import: + DisableImplicitBacktracingModuleImport = true; + break; + case OPT_UNKNOWN: llvm::errs() << "error: unknown argument: " << InputArg->getAsString(ParsedArgs) << '\n' diff --git a/tools/SourceKit/tools/sourcekitd-test/TestOptions.h b/tools/SourceKit/tools/sourcekitd-test/TestOptions.h index 2b080fa12acba..d8b9868dc28c9 100644 --- a/tools/SourceKit/tools/sourcekitd-test/TestOptions.h +++ b/tools/SourceKit/tools/sourcekitd-test/TestOptions.h @@ -133,6 +133,8 @@ struct TestOptions { bool measureInstructions = false; bool DisableImplicitConcurrencyModuleImport = false; bool DisableImplicitStringProcessingModuleImport = false; + bool EnableImplicitBacktracingModuleImport = false; + bool DisableImplicitBacktracingModuleImport = false; llvm::Optional CompletionCheckDependencyInterval; unsigned repeatRequest = 1; struct VFSFile { diff --git a/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp b/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp index 003da68497855..22aef88e5ba81 100644 --- a/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp +++ b/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp @@ -1161,6 +1161,20 @@ static int handleTestInvocation(TestOptions Opts, TestOptions &InitOpts) { sourcekitd_request_array_set_string(Args, SOURCEKITD_ARRAY_APPEND, "-disable-implicit-string-processing-module-import"); } + if (Opts.EnableImplicitBacktracingModuleImport && + !compilerArgsAreClang) { + sourcekitd_request_array_set_string(Args, SOURCEKITD_ARRAY_APPEND, + "-Xfrontend"); + sourcekitd_request_array_set_string(Args, SOURCEKITD_ARRAY_APPEND, + "-enable-implicit-backtracing-module-import"); + } + if (Opts.DisableImplicitBacktracingModuleImport && + !compilerArgsAreClang) { + sourcekitd_request_array_set_string(Args, SOURCEKITD_ARRAY_APPEND, + "-Xfrontend"); + sourcekitd_request_array_set_string(Args, SOURCEKITD_ARRAY_APPEND, + "-disable-implicit-backtracing-module-import"); + } for (auto Arg : Opts.CompilerArgs) sourcekitd_request_array_set_string(Args, SOURCEKITD_ARRAY_APPEND, Arg); diff --git a/tools/swift-ide-test/swift-ide-test.cpp b/tools/swift-ide-test/swift-ide-test.cpp index cff8c9fd2caf5..2d87297ea00c6 100644 --- a/tools/swift-ide-test/swift-ide-test.cpp +++ b/tools/swift-ide-test/swift-ide-test.cpp @@ -807,6 +807,16 @@ DisableImplicitStringProcessingImport("disable-implicit-string-processing-module llvm::cl::desc("Disable implicit import of _StringProcessing module"), llvm::cl::init(false)); +static llvm::cl::opt +EnableImplicitBacktracingImport("enable-implicit-backtracing-module-import", + llvm::cl::desc("Enable implicit import of _Backtracing module"), + llvm::cl::init(false)); + +static llvm::cl::opt +DisableImplicitBacktracingImport("disable-implicit-backtracing-module-import", + llvm::cl::desc("Disable implicit import of _Backtracing module"), + llvm::cl::init(false)); + static llvm::cl::opt EnableExperimentalNamedOpaqueTypes( "enable-experimental-named-opaque-types", llvm::cl::desc("Enable experimental support for named opaque result types"), @@ -4345,6 +4355,14 @@ int main(int argc, char *argv[]) { if (options::DisableImplicitStringProcessingImport) { InitInvok.getLangOptions().DisableImplicitStringProcessingModuleImport = true; } + if (options::DisableImplicitBacktracingImport) { + InitInvok.getLangOptions().DisableImplicitBacktracingModuleImport = true; + } else if (options::EnableImplicitBacktracingImport) { + InitInvok.getLangOptions().DisableImplicitBacktracingModuleImport = false; + } else { + InitInvok.getLangOptions().DisableImplicitBacktracingModuleImport = true; + } + if (options::EnableExperimentalNamedOpaqueTypes) { InitInvok.getLangOptions().Features.insert(Feature::NamedOpaqueTypes); } From ad490b7c71e681fe295158d0018a19ac05b40933 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Mon, 13 Feb 2023 15:35:57 +0000 Subject: [PATCH 17/22] [Backtracing] Add `swift-backtrace` to the build. This is Swift's external backtracer. It's written in Swift, and it's responsible for generating nice backtraces when Swift programs crash. It makes use of the new `_Backtracing` library, which is also (mostly, aside from some assembly language) implemented in Swift. rdar://103442000 --- stdlib/public/CMakeLists.txt | 2 + stdlib/public/libexec/CMakeLists.txt | 1 + stdlib/public/libexec/README.txt | 8 + .../libexec/swift-backtrace/AnsiColor.swift | 159 +++ .../libexec/swift-backtrace/CMakeLists.txt | 12 + .../libexec/swift-backtrace/Target.swift | 346 +++++ .../libexec/swift-backtrace/Themes.swift | 169 +++ .../libexec/swift-backtrace/Utils.swift | 139 +++ .../public/libexec/swift-backtrace/main.swift | 1109 +++++++++++++++++ test/Backtracing/Crash.swift | 178 +++ test/Backtracing/CrashWithThunk.swift | 66 + test/Backtracing/Overflow.swift | 110 ++ test/Backtracing/StackOverflow.swift | 212 ++++ test/Backtracing/SymbolicatedBacktrace.swift | 50 + .../SymbolicatedBacktraceInline.swift | 50 + 15 files changed, 2611 insertions(+) create mode 100644 stdlib/public/libexec/CMakeLists.txt create mode 100644 stdlib/public/libexec/README.txt create mode 100644 stdlib/public/libexec/swift-backtrace/AnsiColor.swift create mode 100644 stdlib/public/libexec/swift-backtrace/CMakeLists.txt create mode 100644 stdlib/public/libexec/swift-backtrace/Target.swift create mode 100644 stdlib/public/libexec/swift-backtrace/Themes.swift create mode 100644 stdlib/public/libexec/swift-backtrace/Utils.swift create mode 100644 stdlib/public/libexec/swift-backtrace/main.swift create mode 100644 test/Backtracing/Crash.swift create mode 100644 test/Backtracing/CrashWithThunk.swift create mode 100644 test/Backtracing/Overflow.swift create mode 100644 test/Backtracing/StackOverflow.swift create mode 100644 test/Backtracing/SymbolicatedBacktrace.swift create mode 100644 test/Backtracing/SymbolicatedBacktraceInline.swift diff --git a/stdlib/public/CMakeLists.txt b/stdlib/public/CMakeLists.txt index 867ce013b05f4..c3e12f498274a 100644 --- a/stdlib/public/CMakeLists.txt +++ b/stdlib/public/CMakeLists.txt @@ -176,3 +176,5 @@ if(SWIFT_BUILD_SDK_OVERLAY) add_subdirectory(Windows) endif() endif() + +add_subdirectory(libexec) diff --git a/stdlib/public/libexec/CMakeLists.txt b/stdlib/public/libexec/CMakeLists.txt new file mode 100644 index 0000000000000..b25c2ebf5806f --- /dev/null +++ b/stdlib/public/libexec/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(swift-backtrace) diff --git a/stdlib/public/libexec/README.txt b/stdlib/public/libexec/README.txt new file mode 100644 index 0000000000000..5ce4dd545b48f --- /dev/null +++ b/stdlib/public/libexec/README.txt @@ -0,0 +1,8 @@ +What is this? +============= + +The libexec directory contains the source code for auxiliary executables that +ship with the Swift runtime, as opposed to being part of the toolchain. + +On UNIX-like platforms, if the runtime is installed in /usr/lib/swift (for +instance), these are things that you would expect to find in /usr/libexec/swift. diff --git a/stdlib/public/libexec/swift-backtrace/AnsiColor.swift b/stdlib/public/libexec/swift-backtrace/AnsiColor.swift new file mode 100644 index 0000000000000..04fac53aa0143 --- /dev/null +++ b/stdlib/public/libexec/swift-backtrace/AnsiColor.swift @@ -0,0 +1,159 @@ +//===--- AnsiColor.swift - ANSI formatting control codes ------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 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 +// +//===----------------------------------------------------------------------===// +// +// Provides ANSI support via Swift string interpolation. +// +//===----------------------------------------------------------------------===// + +enum AnsiColor { + case normal + case black + case red + case green + case yellow + case blue + case magenta + case cyan + case white + case gray + case brightRed + case brightGreen + case brightYellow + case brightBlue + case brightMagenta + case brightCyan + case brightWhite + case rgb(r: Int, g: Int, b: Int) + case grayscale(Int) + + var foregroundCode: String { + switch self { + case .normal: return "39" + case .black: return "30" + case .red: return "31" + case .green: return "32" + case .yellow: return "33" + case .blue: return "34" + case .cyan: return "35" + case .magenta: return "36" + case .white: return "37" + case .gray: return "90" + case .brightRed: return "91" + case .brightGreen: return "92" + case .brightYellow: return "93" + case .brightBlue: return "94" + case .brightCyan: return "95" + case .brightMagenta: return "96" + case .brightWhite: return "97" + case let .rgb(r, g, b): + let ndx = 16 + 36 * r + 6 * g + b + return "38;5;\(ndx)" + case let .grayscale(g): + let ndx = 232 + g + return "38;5;\(ndx)" + } + } + + var backgroundCode: String { + switch self { + case .normal: return "49" + case .black: return "40" + case .red: return "41" + case .green: return "42" + case .yellow: return "43" + case .blue: return "44" + case .cyan: return "45" + case .magenta: return "46" + case .white: return "47" + case .gray: return "100" + case .brightRed: return "101" + case .brightGreen: return "102" + case .brightYellow: return "103" + case .brightBlue: return "104" + case .brightCyan: return "105" + case .brightMagenta: return "106" + case .brightWhite: return "107" + case let .rgb(r, g, b): + let ndx = 16 + 36 * r + 6 * g + b + return "48;5;\(ndx)" + case let .grayscale(g): + let ndx = 232 + g + return "48;5;\(ndx)" + } + } +} + +enum AnsiWeight { + case normal + case bold + case faint + + var code: String { + switch self { + case .normal: return "22" + case .bold: return "1" + case .faint: return "2" + } + } +} + +enum AnsiAttribute { + case fg(AnsiColor) + case bg(AnsiColor) + case weight(AnsiWeight) + case inverse(Bool) +} + +extension DefaultStringInterpolation { + mutating func appendInterpolation(ansi attrs: AnsiAttribute...) { + var code = "\u{1b}[" + var first = true + for attr in attrs { + if first { + first = false + } else { + code += ";" + } + + switch attr { + case let .fg(color): + code += color.foregroundCode + case let .bg(color): + code += color.backgroundCode + case let .weight(weight): + code += weight.code + case let .inverse(enabled): + if enabled { + code += "7" + } else { + code += "27" + } + } + } + code += "m" + + appendInterpolation(code) + } + + mutating func appendInterpolation(fg: AnsiColor) { + return appendInterpolation(ansi: .fg(fg)) + } + mutating func appendInterpolation(bg: AnsiColor) { + return appendInterpolation(ansi: .bg(bg)) + } + mutating func appendInterpolation(weight: AnsiWeight) { + return appendInterpolation(ansi: .weight(weight)) + } + mutating func appendInterpolation(inverse: Bool) { + return appendInterpolation(ansi: .inverse(inverse)) + } +} diff --git a/stdlib/public/libexec/swift-backtrace/CMakeLists.txt b/stdlib/public/libexec/swift-backtrace/CMakeLists.txt new file mode 100644 index 0000000000000..a7bdcf78fde17 --- /dev/null +++ b/stdlib/public/libexec/swift-backtrace/CMakeLists.txt @@ -0,0 +1,12 @@ +add_swift_target_executable(swift-backtrace BUILD_WITH_STDLIB + main.swift + AnsiColor.swift + Target.swift + Themes.swift + Utils.swift + + SWIFT_MODULE_DEPENDS + _Backtracing + + INSTALL_IN_COMPONENT stdlib + COMPILE_FLAGS -parse-as-library) diff --git a/stdlib/public/libexec/swift-backtrace/Target.swift b/stdlib/public/libexec/swift-backtrace/Target.swift new file mode 100644 index 0000000000000..a0105626a44c9 --- /dev/null +++ b/stdlib/public/libexec/swift-backtrace/Target.swift @@ -0,0 +1,346 @@ +//===--- Target.swift - Represents a process we are inspecting ------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 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 +// +//===----------------------------------------------------------------------===// +// +// Defines `Target`, which represents the process we are inspecting. +// There are a lot of system specifics in this file! +// +//===----------------------------------------------------------------------===// + +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + +import Darwin +import Darwin.Mach + +import _Backtracing +@_spi(Internal) import _Backtracing +@_spi(Contexts) import _Backtracing +@_spi(MemoryReaders) import _Backtracing + +import _SwiftBacktracingShims + +#if arch(x86_64) +typealias MContext = darwin_x86_64_mcontext +#elseif arch(arm64) || arch(arm64_32) +typealias MContext = darwin_arm64_mcontext +#else +#error("You need to define MContext for this architecture") +#endif + +extension thread_extended_info { + var pth_swiftName: String { + withUnsafePointer(to: pth_name) { ptr in + let len = strnlen(ptr, Int(MAXTHREADNAMESIZE)) + return String(decoding: UnsafeRawBufferPointer(start: ptr, count: Int(len)), + as: UTF8.self) + } + } +} + +struct TargetThread { + typealias ThreadID = UInt64 + + var id: ThreadID + var context: HostContext? + var name: String + var backtrace: SymbolicatedBacktrace +} + +class Target { + typealias Address = UInt64 + + var pid: pid_t + var name: String + var signal: UInt64 + var faultAddress: Address + var crashingThread: TargetThread.ThreadID + + var task: task_t + var images: [Backtrace.Image] = [] + var sharedCacheInfo: Backtrace.SharedCacheInfo? + + var threads: [TargetThread] = [] + var crashingThreadNdx: Int = -1 + + var signalName: String { + switch signal { + case UInt64(SIGQUIT): return "SIGQUIT" + case UInt64(SIGABRT): return "SIGABRT" + case UInt64(SIGBUS): return "SIGBUS" + case UInt64(SIGFPE): return "SIGFPE" + case UInt64(SIGILL): return "SIGILL" + case UInt64(SIGSEGV): return "SIGSEGV" + case UInt64(SIGTRAP): return "SIGTRAP" + default: return "\(signal)" + } + } + + var signalDescription: String { + switch signal { + case UInt64(SIGQUIT): return "Terminated" + case UInt64(SIGABRT): return "Aborted" + case UInt64(SIGBUS): return "Bus error" + case UInt64(SIGFPE): return "Floating point exception" + case UInt64(SIGILL): return "Illegal instruction" + case UInt64(SIGSEGV): return "Bad pointer dereference" + case UInt64(SIGTRAP): return "System trap" + default: + return "Signal \(signal)" + } + } + + var reader: RemoteMemoryReader + + var mcontext: MContext + + static func getParentTask() -> task_t? { + var ports: mach_port_array_t? = nil + var portCount: mach_msg_type_number_t = 0 + + // For some reason, we can't pass a task read port this way, but we + // *can* pass the control port. So do that and then ask for a read port + // before immediately dropping the control port from this process. + + let kr = mach_ports_lookup(mach_task_self_, &ports, &portCount) + if kr != KERN_SUCCESS { + return nil + } + + if let ports = ports, portCount != 0 { + var taskPort: mach_port_t = 0 + let kr = task_get_special_port(ports[0], TASK_READ_PORT, &taskPort) + if kr != KERN_SUCCESS { + mach_port_deallocate(mach_task_self_, ports[0]) + return nil + } + mach_port_deallocate(mach_task_self_, ports[0]) + return task_t(taskPort) + } else { + return nil + } + } + + static func getProcessName(pid: pid_t) -> String { + let buffer = UnsafeMutableBufferPointer.allocate(capacity: 4096) + defer { + buffer.deallocate() + } + let ret = proc_name(pid, buffer.baseAddress, UInt32(buffer.count)) + if ret <= 0 { + return "" + } else { + return String(decoding: buffer[0.. ()) throws { + #if os(macOS) + return try withTemporaryDirectory(pattern: "/tmp/backtrace.XXXXXXXX") { + tmpdir in + + let cmdfile = "\(tmpdir)/lldb.command" + guard let fp = fopen(cmdfile, "wt") else { + throw PosixError(errno: errno) + } + if fputs(""" + #!/bin/bash + clear + echo "Once LLDB has attached, return to the other window and press any key" + echo "" + xcrun lldb --attach-pid \(pid) -o c + """, fp) == EOF { + throw PosixError(errno: errno) + } + if fclose(fp) != 0 { + throw PosixError(errno: errno) + } + if chmod(cmdfile, S_IXUSR|S_IRUSR) != 0 { + throw PosixError(errno: errno) + } + + try spawn("/usr/bin/open", args: ["open", cmdfile]) + + body() + } + #else + print(""" + From another shell, please run + + lldb --attach-pid \(target.pid) -o c + """) + body() + #endif + } +} + +private func mach_thread_info(_ thread: thread_t, + _ flavor: CInt, + _ result: inout T) -> kern_return_t { + var count: mach_msg_type_number_t + = mach_msg_type_number_t(MemoryLayout.stride + / MemoryLayout.stride) + + return withUnsafeMutablePointer(to: &result) { ptr in + ptr.withMemoryRebound(to: natural_t.self, capacity: Int(count)) { intPtr in + return thread_info(thread, + thread_flavor_t(flavor), + intPtr, + &count) + } + } +} + +#endif // os(macOS) || os(iOS) || os(watchOS) || os(tvOS) diff --git a/stdlib/public/libexec/swift-backtrace/Themes.swift b/stdlib/public/libexec/swift-backtrace/Themes.swift new file mode 100644 index 0000000000000..1dc6f88da1a29 --- /dev/null +++ b/stdlib/public/libexec/swift-backtrace/Themes.swift @@ -0,0 +1,169 @@ +//===--- Themes.swift - Represents a process we are inspecting ------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2022 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 +// +//===----------------------------------------------------------------------===// +// +// Defines the `Theme` struct that we use for color support. +// +//===----------------------------------------------------------------------===// + +@_spi(Formatting) import _Backtracing + +protocol ErrorAndWarningTheme { + func crashReason(_ s: String) -> String + func error(_ s: String) -> String + func warning(_ s: String) -> String + func info(_ s: String) -> String +} + +extension ErrorAndWarningTheme { + public func crashReason(_ s: String) -> String { return "*** \(s) ***" } + public func error(_ s: String) -> String { return "!!! error: \(s)" } + public func warning(_ s: String) -> String { return "/!\\ warning: \(s)" } + public func info(_ s: String) -> String { return "(i) \(s)" } +} + +protocol PromptTheme { + func prompt(_ s: String) -> String +} + +extension PromptTheme { + public func prompt(_ s: String) -> String { return s } +} + +protocol MemoryDumpTheme { + func address(_ s: String) -> String + func data(_ s: String) -> String + func printable(_ s: String) -> String + func nonPrintable(_ s: String) -> String +} + +extension MemoryDumpTheme { + public func address(_ s: String) -> String { return s } + public func data(_ s: String) -> String { return s } + public func printable(_ s: String) -> String { return s } + public func nonPrintable(_ s: String) -> String { return s } +} + +protocol RegisterDumpTheme : MemoryDumpTheme { + func register(_ s: String) -> String + func hexValue(_ s: String) -> String + func decimalValue(_ s: String) -> String + func flags(_ s: String) -> String +} + +extension RegisterDumpTheme { + public func register(_ s: String) -> String { return s } + public func hexValue(_ s: String) -> String { return s } + public func decimalValue(_ s: String) -> String { return s } + public func flags(_ s: String) -> String { return s } +} + +typealias Theme = BacktraceFormattingTheme & ErrorAndWarningTheme & + PromptTheme & MemoryDumpTheme & RegisterDumpTheme + +enum Themes { + + struct Plain: Theme { + } + + struct Color: Theme { + public func frameIndex(_ s: String) -> String { + return "\(fg: .gray)\(s)\(fg: .normal)" + } + public func programCounter(_ s: String) -> String { + return "\(fg: .green)\(s)\(fg: .normal)" + } + public func frameAttribute(_ s: String) -> String { + return "\(fg: .blue)[\(s)]\(fg: .normal)" + } + + public func symbol(_ s: String) -> String { + return "\(fg: .brightMagenta)\(s)\(fg: .normal)" + } + public func offset(_ s: String) -> String { + return "\(fg: .white)\(s)\(fg: .normal)" + } + public func sourceLocation(_ s: String) -> String { + return "\(fg: .yellow)\(s)\(fg: .normal)" + } + public func lineNumber(_ s: String) -> String { + return "\(fg: .gray)\(s)\(fg: .normal)" + } + public func code(_ s: String) -> String { + return "\(s)" + } + public func crashedLine(_ s: String) -> String { + return "\(bg: .grayscale(2))\(s)\(bg: .normal)" + } + public func crashLocation(_ s: String) -> String { + return "\(fg: .brightRed)\(s)\(fg: .normal)" + } + public func imageName(_ s: String) -> String { + return "\(fg: .cyan)\(s)\(fg: .normal)" + } + public func imageAddressRange(_ s: String) -> String { + return "\(fg: .green)\(s)\(fg: .normal)" + } + public func imageBuildID(_ s: String) -> String { + return "\(fg: .white)\(s)\(fg: .normal)" + } + public func imagePath(_ s: String) -> String { + return "\(fg: .gray)\(s)\(fg: .normal)" + } + + public func prompt(_ s: String) -> String { + return "\(fg: .gray)\(s)\(fg: .normal)" + } + + public func address(_ s: String) -> String { + return "\(fg: .green)\(s)\(fg: .normal)" + } + public func data(_ s: String) -> String { + return s + } + public func printable(_ s: String) -> String { + return s + } + public func nonPrintable(_ s: String) -> String { + return "\(fg: .gray)\(s)\(fg: .normal)" + } + + public func register(_ s: String) -> String { + return s + } + public func hexValue(_ s: String) -> String { + return "\(fg: .green)\(s)\(fg: .normal)" + } + public func decimalValue(_ s: String) -> String { + return "\(fg: .green)\(s)\(fg: .normal)" + } + public func flags(_ s: String) -> String { + return "\(fg: .magenta)\(s)\(fg: .normal)" + } + + public func crashReason(_ s: String) -> String { + return "💣 \(fg: .brightRed)\(s)\(fg: .normal)" + } + public func error(_ s: String) -> String { + return "🛑 \(fg: .brightRed)error: \(s)\(fg: .normal)" + } + public func warning(_ s: String) -> String { + return "⚠️ \(fg: .brightYellow)warning: \(s)\(fg: .normal)" + } + public func info(_ s: String) -> String { + return "ℹ️ \(s)" + } + } + + static let plain = Plain() + static let color = Color() + +} diff --git a/stdlib/public/libexec/swift-backtrace/Utils.swift b/stdlib/public/libexec/swift-backtrace/Utils.swift new file mode 100644 index 0000000000000..be28d98e0c545 --- /dev/null +++ b/stdlib/public/libexec/swift-backtrace/Utils.swift @@ -0,0 +1,139 @@ +//===--- Utils.swift - Utility functions ----------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// +// +// Utility functions that are used in the swift-backtrace program. +// +//===----------------------------------------------------------------------===// + +#if canImport(Darwin) +import Darwin.C +#elseif canImport(Glibc) +import Glibc +#elseif canImport(MSVCRT) +import MSVCRT +#endif + +import Swift + +internal func hex(_ value: T, + withPrefix: Bool = true) -> String { + let digits = String(value, radix: 16) + let padTo = value.bitWidth / 4 + let padding = digits.count >= padTo ? "" : String(repeating: "0", + count: padTo - digits.count) + let prefix = withPrefix ? "0x" : "" + + return "\(prefix)\(padding)\(digits)" +} + +internal func hex(_ bytes: [UInt8]) -> String { + return bytes.map{ hex($0, withPrefix: false) }.joined(separator: "") +} + +internal func parseUInt64(_ s: S) -> UInt64? { + if s.hasPrefix("0x") { + return UInt64(s.dropFirst(2), radix: 16) + } else if s.hasPrefix("0b") { + return UInt64(s.dropFirst(2), radix: 2) + } else if s.hasPrefix("0o") { + return UInt64(s.dropFirst(2), radix: 8) + } else { + return UInt64(s, radix: 10) + } +} + +struct PosixError: Error { + var errno: Int32 + + var desription: String { + return String(cString: strerror(self.errno)) + } +} + +internal func recursiveRemoveContents(_ dir: String) throws { + guard let dirp = opendir(dir) else { + throw PosixError(errno: errno) + } + defer { + closedir(dirp) + } + while let dp = readdir(dirp) { + let name: String = + withUnsafePointer(to: &dp.pointee.d_name) { +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + let len = Int(dp.pointee.d_namlen) +#else + let len = Int(strlen($0)) +#endif + return String(decoding: UnsafeRawBufferPointer(start: $0, + count: len), + as: UTF8.self) + } + if name == "." || name == ".." { + continue + } + let fullPath = "\(dir)/\(name)" + if dp.pointee.d_type == DT_DIR { + try recursiveRemove(fullPath) + } else { + if unlink(fullPath) != 0 { + throw PosixError(errno: errno) + } + } + } +} + +internal func recursiveRemove(_ dir: String) throws { + try recursiveRemoveContents(dir) + + if rmdir(dir) != 0 { + throw PosixError(errno: errno) + } +} + +internal func withTemporaryDirectory(pattern: String, shouldDelete: Bool = true, + body: (String) throws -> ()) throws { + var buf = Array(pattern.utf8) + buf.append(0) + + guard let dir = buf.withUnsafeMutableBufferPointer({ + if let ptr = mkdtemp($0.baseAddress!) { + return String(cString: ptr) + } + return nil + }) else { + throw PosixError(errno: errno) + } + + defer { + if shouldDelete { + try? recursiveRemove(dir) + } + } + + try body(dir) +} + +internal func spawn(_ path: String, args: [String]) throws { + var cargs = args.map{ strdup($0) } + cargs.append(nil) + let result = cargs.withUnsafeBufferPointer{ + posix_spawn(nil, path, nil, nil, $0.baseAddress!, nil) + } + for arg in cargs { + free(arg) + } + if result != 0 { + throw PosixError(errno: errno) + } +} + diff --git a/stdlib/public/libexec/swift-backtrace/main.swift b/stdlib/public/libexec/swift-backtrace/main.swift new file mode 100644 index 0000000000000..ddde90f23e78e --- /dev/null +++ b/stdlib/public/libexec/swift-backtrace/main.swift @@ -0,0 +1,1109 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2023 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 +// +//===----------------------------------------------------------------------===// + +#if os(macOS) + +#if canImport(Darwin) +import Darwin.C +#elseif canImport(Glibc) +import Glibc +#elseif canImport(MSVCRT) +import MSVCRT +#endif + +@_spi(Formatting) import _Backtracing +@_spi(Contexts) import _Backtracing +@_spi(Registers) import _Backtracing +@_spi(MemoryReaders) import _Backtracing + +@main +internal struct SwiftBacktrace { + enum UnwindAlgorithm { + case fast + case precise + } + + enum Preset { + case none + case friendly + case medium + case full + } + + enum ImagesToShow { + case none + case all + case mentioned + } + + enum RegistersToShow { + case none + case all + case crashedOnly + } + + struct Arguments { + var unwindAlgorithm: UnwindAlgorithm = .precise + var demangle = false + var interactive = false + var color = false + var timeout = 30 + var preset: Preset = .none + var threads: Bool? = nil + var registers: RegistersToShow? = nil + var crashInfo: UInt64? = nil + var showImages: ImagesToShow? = nil + var limit: Int? = 64 + var top = 16 + var sanitize: Bool? = nil + } + + static var args = Arguments() + static var formattingOptions = BacktraceFormattingOptions() + + static var target: Target? = nil + static var currentThread: Int = 0 + + static var theme: any Theme { + if args.color { + return Themes.color + } else { + return Themes.plain + } + } + + static func usage() { + print(""" +usage: swift-backtrace [--unwind ] [--demangle []] [--interactive []] [--color []] [--timeout ] [--preset ] [--threads []] [--registers ] [--images ] --crashinfo + +Generate a backtrace for the parent process. + +--unwind +-u Set the unwind algorithm to use. Supported algorithms + are "fast" and "precise". + +--demangle [] +-d [] Set whether or not to demangle identifiers. + +--interactive [] +-i [] Set whether to be interactive. + +--color [] +-c [] Set whether to use ANSI color in the output. + +--timeout +-t Set how long to wait for interaction. + +--preset +-p Set the backtrace format (by preset). Options are + "friendly", "medium" and "full". + +--threads [] +-h [] Set whether or not to show all threads. + +--registers +-r Set which registers dumps to show. Options are "none", + "all" and "crashed". + +--images +-m Set which images to list. Options are "none", "all" + and "mentioned". + +--limit +-l Set the limit on the number of frames to capture. + Can be set to "none" to disable the limit. + +--top +-T Set the minimum number of frames to capture at the top + of the stack. This is used with limit to ensure that + you capture sufficient frames to understand deep traces. + +--sanitize [] +-s [] Set whether or not to sanitize paths. + +--crashinfo +-a Provide a pointer to a platform specific CrashInfo + structure. should be in hexadecimal. +""") + } + + static func parseBool(_ s: some StringProtocol) -> Bool { + let lowered = s.lowercased() + return lowered == "yes" || lowered == "y" || + lowered == "true" || lowered == "t" || lowered == "on" + } + + static func handleArgument(_ arg: String, value: String?) { + switch arg { + case "-?", "--help": + usage() + exit(0) + case "-u", "--unwind": + if let v = value { + switch v.lowercased() { + case "fast": + args.unwindAlgorithm = .fast + case "precise": + args.unwindAlgorithm = .precise + default: + print("swift-backtrace: unknown unwind algorithm '\(v)'") + usage() + exit(1) + } + } else { + print("swift-backtrace: missing unwind algorithm") + usage() + exit(1) + } + case "-d", "--demangle": + if let v = value { + args.demangle = parseBool(v) + } else { + args.demangle = true + } + case "-i", "--interactive": + if let v = value { + args.interactive = parseBool(v) + } else { + args.interactive = true + } + case "-c", "--color": + if let v = value { + args.color = parseBool(v) + } else { + args.color = true + } + case "-t", "--timeout": + if let v = value { + if let secs = Int(v), secs >= 0 { + args.timeout = secs + } else { + print("swift-backtrace: bad timeout '\(v)'") + } + } else { + print("swift-backtrace: missing timeout value") + usage() + exit(1) + } + case "-p", "--preset": + if let v = value { + switch v.lowercased() { + case "friendly": + args.preset = .friendly + case "medium": + args.preset = .medium + case "full": + args.preset = .full + default: + print("swift-backtrace: unknown preset '\(v)'") + usage() + exit(1) + } + } else { + print("swift-backtrace: missing preset name") + usage() + exit(1) + } + case "-h", "--threads": + if let v = value { + if v.lowercased() != "preset" { + args.threads = parseBool(v) + } + } else { + args.threads = true + } + case "-r", "--registers": + if let v = value { + switch v.lowercased() { + case "preset": + break + case "none": + args.registers = RegistersToShow.none + case "all": + args.registers = .all + case "crashed": + args.registers = .crashedOnly + default: + print("swift-backtrace: unknown registers setting '\(v)'") + usage() + exit(1) + } + } else { + print("swift-backtrace: missing registers setting") + usage() + exit(1) + } + case "-m", "--images": + if let v = value { + switch v.lowercased() { + case "preset": + break + case "none": + args.showImages = ImagesToShow.none + case "all": + args.showImages = .all + case "mentioned": + args.showImages = .mentioned + default: + print("swift-backtrace: unknown images setting '\(v)'") + usage() + exit(1) + } + } else { + print("swift-backtrace: missing images setting") + usage() + exit(1) + } + case "-l", "--limit": + if let v = value { + if v.lowercased() == "none" { + args.limit = nil + } else if let limit = Int(v), limit > 0 { + args.limit = limit + } else { + print("swift-backtrace: bad limit value \(v)") + } + } else { + print("swift-backtrace: missing limit value") + usage() + exit(1) + } + case "-T", "--top": + if let v = value { + if let top = Int(v), top >= 0 { + args.top = top + } else { + print("swift-backtrace: bad top value \(v)") + } + } else { + print("swift-backtrace: missing top value") + usage() + exit(1) + } + case "-s", "--sanitize": + if let v = value { + args.sanitize = parseBool(v) + } else { + args.sanitize = true + } + case "-a", "--crashinfo": + if let v = value { + if let a = UInt64(v, radix: 16) { + args.crashInfo = a + } else { + print("swift-backtrace: bad pointer '\(v)'") + usage() + exit(1) + } + } else { + print("swift-backtrace: missing pointer value") + usage() + exit(1) + } + default: + print("swift-backtrace: unknown argument '\(arg)'") + usage() + exit(1) + } + } + + static func main() { + parseArguments() + + guard let crashInfoAddr = args.crashInfo else { + print("swift-backtrace: --crashinfo is not optional") + usage() + exit(1) + } + + // Set-up the backtrace formatting options + switch args.preset { + case .friendly, .none: + formattingOptions = + .skipRuntimeFailures(true) + .showAddresses(false) + .showSourceCode(true) + .showFrameAttributes(false) + .sanitizePaths(args.sanitize ?? false) + if args.threads == nil { + args.threads = false + } + if args.registers == nil { + args.registers = RegistersToShow.none + } + if args.showImages == nil { + args.showImages = ImagesToShow.none + } + case .medium: + formattingOptions = + .skipRuntimeFailures(true) + .showSourceCode(true) + .showFrameAttributes(true) + .sanitizePaths(args.sanitize ?? true) + if args.threads == nil { + args.threads = false + } + if args.registers == nil { + args.registers = .crashedOnly + } + if args.showImages == nil { + args.showImages = .mentioned + } + case .full: + formattingOptions = + .skipRuntimeFailures(false) + .skipThunkFunctions(false) + .skipSystemFrames(false) + .sanitizePaths(args.sanitize ?? true) + if args.threads == nil { + args.threads = true + } + if args.registers == nil { + args.registers = .crashedOnly + } + if args.showImages == nil { + args.showImages = .mentioned + } + } + formattingOptions = formattingOptions.demangle(args.demangle) + + // We never use the showImages option; if we're going to show images, we + // want to do it *once* for all the backtraces we showed. + formattingOptions = formattingOptions.showImages(.none) + + target = Target(crashInfoAddr: crashInfoAddr, + limit: args.limit, top: args.top) + + currentThread = target!.crashingThreadNdx + + printCrashLog() + + print("") + + if args.interactive { + // Make sure we're line buffered + setvbuf(stdout, nil, _IOLBF, 0) + + while let ch = waitForKey("Press space to interact, D to debug, or any other key to quit", + timeout: args.timeout) { + switch UInt8(ch) { + case UInt8(ascii: " "): + interactWithUser() + exit(0) + case UInt8(ascii: "D"), UInt8(ascii: "d"): + startDebugger() + default: + exit(0) + } + } + } + + } + + // Parse the command line arguments; we can't use swift-argument-parser + // from here because that would create a dependency problem, so we do + // it manually. + static func parseArguments() { + var currentArg: String? = nil + for arg in CommandLine.arguments[1...] { + if arg.hasPrefix("-") { + if let key = currentArg { + handleArgument(key, value: nil) + } + currentArg = arg + } else { + if let key = currentArg { + handleArgument(key, value: arg) + currentArg = nil + } else if arg != "" { + print("swift-backtrace: unexpected argument '\(arg)'") + usage() + exit(1) + } + } + } + if let key = currentArg { + handleArgument(key, value: nil) + } + } + + #if os(Linux) || os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + static func setRawMode() -> termios { + var oldAttrs = termios() + tcgetattr(0, &oldAttrs) + + var newAttrs = oldAttrs + newAttrs.c_lflag &= ~(UInt(ICANON) | UInt(ECHO)) + tcsetattr(0, TCSANOW, &newAttrs) + + return oldAttrs + } + + static func resetInputMode(mode: termios) { + var theMode = mode + tcsetattr(0, TCSANOW, &theMode) + } + + static func waitForKey(_ message: String, timeout: Int?) -> Int32? { + let oldMode = setRawMode() + + defer { + print("\r\u{1b}[0K", terminator: "") + fflush(stdout) + resetInputMode(mode: oldMode) + } + + if let timeout = timeout { + var remaining = timeout + + while true { + print("\r\(message) (\(remaining)s) ", terminator: "") + fflush(stdout) + + var pfd = pollfd(fd: 0, events: Int16(POLLIN), revents: 0) + + let ret = poll(&pfd, 1, 1000) + if ret == 0 { + remaining -= 1 + if remaining == 0 { + break + } + continue + } else if ret < 0 { + break + } + + return getchar() + } + } else { + print("\r\(message)", terminator: "") + fflush(stdout) + return getchar() + } + + return nil + } + #elseif os(Windows) + static func waitForKey(_ message: String, timeout: Int?) -> Int32? { + // ###TODO + return nil + } + #endif + + static func backtraceFormatter() -> BacktraceFormatter { + var terminalSize = winsize(ws_row: 24, ws_col: 80, + ws_xpixel: 1024, ws_ypixel: 768) + _ = ioctl(0, TIOCGWINSZ, &terminalSize) + + return BacktraceFormatter(formattingOptions + .theme(theme) + .width(Int(terminalSize.ws_col))) + } + + static func printCrashLog() { + guard let target = target else { + print("swift-backtrace: unable to get target") + return + } + + let crashingThread = target.threads[target.crashingThreadNdx] + + let description: String + + if let failure = crashingThread.backtrace.swiftRuntimeFailure { + description = failure + } else { + description = "Program crashed: \(target.signalDescription) at \(hex(target.faultAddress))" + } + + print("") + print(theme.crashReason(description)) + print("") + + var mentionedImages = Set() + let formatter = backtraceFormatter() + + func dump(ndx: Int, thread: TargetThread) { + let crashed = thread.id == target.crashingThread ? " crashed" : "" + let name = !thread.name.isEmpty ? " \"\(thread.name)\"" : "" + print("Thread \(ndx)\(name)\(crashed):\n") + + if args.registers! == .all { + if let context = thread.context { + showRegisters(context) + } else { + print(" " + theme.info("no context for thread \(ndx)")) + } + print("") + } + + let formatted = formatter.format(backtrace: thread.backtrace) + + print(formatted) + + if args.showImages! == .mentioned { + for frame in thread.backtrace.frames { + if formatter.shouldSkip(frame) { + continue + } + if let symbol = frame.symbol, symbol.imageIndex >= 0 { + mentionedImages.insert(symbol.imageIndex) + } + } + } + } + + if args.threads! { + for (ndx, thread) in target.threads.enumerated() { + dump(ndx: ndx, thread: thread) + } + } else { + dump(ndx: target.crashingThreadNdx, thread: crashingThread) + } + + if args.registers! == .crashedOnly { + print("\n\nRegisters:\n") + + if let context = target.threads[target.crashingThreadNdx].context { + showRegisters(context) + } else { + print(theme.info("no context for thread \(target.crashingThreadNdx)")) + } + } + + switch args.showImages! { + case .none: + break + case .mentioned: + let images = mentionedImages.sorted().map{ target.images[$0] } + let omitted = target.images.count - images.count + if omitted > 0 { + print("\n\nImages (\(omitted) omitted):\n") + } else { + print("\n\nImages:\n") + } + print(formatter.format(images: images)) + case .all: + print("\n\nImages:\n") + print(formatter.format(images: target.images)) + } + } + + static func startDebugger() { + guard let target = target else { + return + } + + do { + try target.withDebugger { + + if let ch = waitForKey("Press any key once LLDB is attached, or A to abort", timeout: nil), + ch != UInt8(ascii: "A") && ch != UInt8(ascii: "a") { + exit(0) + } + } + } catch { + print(theme.error("unable to spawn debugger")) + } + } + + static func interactWithUser() { + guard let target = target else { + return + } + + while true { + fflush(stdout) + print(theme.prompt(">>> "), terminator: "") + guard let input = readLine() else { + print("") + break + } + + let cmd = input.split(whereSeparator: { $0.isWhitespace }) + + if cmd.count < 1 { + continue + } + + // ###TODO: We should really replace this with something a little neater + switch cmd[0].lowercased() { + case "exit", "quit": + return + case "debug": + startDebugger() + case "bt", "backtrace": + let formatter = backtraceFormatter() + let backtrace = target.threads[currentThread].backtrace + let formatted = formatter.format(backtrace: backtrace) + + print(formatted) + case "thread": + if cmd.count >= 2 { + if let newThreadNdx = Int(cmd[1]), + newThreadNdx >= 0 && newThreadNdx < target.threads.count { + currentThread = newThreadNdx + } else { + print(theme.error("Bad thread index '\(cmd[1])'")) + break + } + } + + let crashed: String + if currentThread == target.crashingThreadNdx { + crashed = " (crashed)" + } else { + crashed = "" + } + + let thread = target.threads[currentThread] + let backtrace = thread.backtrace + let name = thread.name.isEmpty ? "" : " \(thread.name)" + print("Thread \(currentThread) id=\(thread.id)\(name)\(crashed)\n") + + if let frame = backtrace.frames.drop(while: { + $0.isSwiftRuntimeFailure + }).first { + let formatter = backtraceFormatter() + let formatted = formatter.format(frame: frame) + print("\(formatted)") + } + break + case "reg", "registers": + if let context = target.threads[currentThread].context { + showRegisters(context) + } else { + print(theme.info("no context for thread \(currentThread)")) + } + break + case "mem", "memory": + if cmd.count != 2 && cmd.count != 3 { + print("memory [|+]") + break + } + + guard let startAddress = parseUInt64(cmd[1]) else { + print(theme.error("bad start address \(cmd[1])")) + break + } + + let count: UInt64 + if cmd.count == 3 { + if cmd[2].hasPrefix("+") { + guard let theCount = parseUInt64(cmd[2].dropFirst()) else { + print(theme.error("bad byte count \(cmd[2])")) + break + } + count = theCount + } else { + guard let addr = parseUInt64(cmd[2]) else { + print(theme.error("bad end address \(cmd[2])")) + break + } + if addr < startAddress { + print("End address must be after start address") + break + } + count = addr - startAddress + } + } else { + count = 256 + } + + dumpMemory(at: startAddress, count: count) + break + case "process", "threads": + print("Process \(target.pid) \"\(target.name)\" has \(target.threads.count) thread(s):\n") + + let formatter = backtraceFormatter() + + var rows: [BacktraceFormatter.TableRow] = [] + for (n, thread) in target.threads.enumerated() { + let backtrace = thread.backtrace + + let crashed: String + if n == target.crashingThreadNdx { + crashed = " (crashed)" + } else { + crashed = "" + } + + let selected = currentThread == n ? "▶︎" : " " + let name = thread.name.isEmpty ? "" : " \(thread.name)" + + rows.append(.columns([ selected, + "\(n)", + "id=\(thread.id)\(name)\(crashed)" ])) + if let frame = backtrace.frames.drop(while: { + $0.isSwiftRuntimeFailure + }).first { + + rows += formatter.formatRows(frame: frame).map{ row in + switch row { + case let .columns(columns): + return .columns([ "", "" ] + columns) + default: + return row + } + } + } + } + + let output = BacktraceFormatter.formatTable(rows, + alignments: [ + .left, + .right + ]) + print(output) + case "images": + let formatter = backtraceFormatter() + let images = target.threads[currentThread].backtrace.images + let output = formatter.format(images: images) + + print(output) + case "set": + if cmd.count == 1 { + let limit: String + if let lim = args.limit { + limit = "\(lim)" + } else { + limit = "none" + } + let top = "\(args.top)" + + print(""" + addresses = \(formattingOptions.shouldShowAddresses) + demangle = \(formattingOptions.shouldDemangle) + frame-attrs = \(formattingOptions.shouldShowFrameAttributes) + image-names = \(formattingOptions.shouldShowImageNames) + limit = \(limit) + sanitize = \(formattingOptions.shouldSanitizePaths) + source = \(formattingOptions.shouldShowSourceCode) + source-context = \(formattingOptions.sourceContextLines) + system-frames = \(!formattingOptions.shouldSkipSystemFrames) + thunks = \(!formattingOptions.shouldSkipThunkFunctions) + top = \(top) + """) + } else { + for optval in cmd[1...] { + let parts = optval.split(separator: "=", maxSplits: 1, + omittingEmptySubsequences: false) + if parts.count == 1 { + let option = parts[0] + + switch option { + case "addresses": + print("addresses = \(formattingOptions.shouldShowAddresses)") + case "demangle": + print("demangle = \(formattingOptions.shouldDemangle)") + case "frame-attrs": + print("frame-attrs = \(formattingOptions.shouldShowFrameAttributes)") + case "image-names": + print("image-names = \(formattingOptions.shouldShowImageNames)") + case "limit": + if let limit = args.limit { + print("limit = \(limit)") + } else { + print("limit = none") + } + case "sanitize": + print("sanitize = \(formattingOptions.shouldSanitizePaths)") + case "source": + print("source = \(formattingOptions.shouldShowSourceCode)") + case "source-context": + print("source-context = \(formattingOptions.sourceContextLines)") + case "system-frames": + print("system-frames = \(!formattingOptions.shouldSkipSystemFrames)") + case "thunks": + print("thunks = \(!formattingOptions.shouldSkipThunkFunctions)") + case "top": + print("top = \(args.top)") + + default: + print(theme.error("unknown option '\(option)'")) + } + } else { + let option = parts[0] + let value = parts[1] + var changedBacktrace = false + + switch option { + case "limit": + if value == "none" { + args.limit = nil + changedBacktrace = true + } else if let limit = Int(value), limit > 0 { + args.limit = limit + changedBacktrace = true + } else { + print(theme.error("bad limit value '\(value)'")) + } + + case "top": + if let top = Int(value), top >= 0 { + args.top = top + changedBacktrace = true + } else { + print(theme.error("bad top value '\(value)'")) + } + + case "source": + formattingOptions = + formattingOptions.showSourceCode(parseBool(value), + contextLines: formattingOptions.sourceContextLines) + + case "source-context": + if let lines = Int(value), lines >= 0 { + formattingOptions = + formattingOptions.showSourceCode(formattingOptions.shouldShowSourceCode, + contextLines: lines) + } else { + print(theme.error("bad source-context value '\(value)'")) + } + + case "thunks": + formattingOptions = + formattingOptions.skipThunkFunctions(!parseBool(value)) + + case "system-frames": + formattingOptions = + formattingOptions.skipSystemFrames(!parseBool(value)) + + case "frame-attrs": + formattingOptions = + formattingOptions.showFrameAttributes(parseBool(value)) + + case "addresses": + formattingOptions = + formattingOptions.showAddresses(parseBool(value)) + + case "sanitize": + formattingOptions = + formattingOptions.sanitizePaths(parseBool(value)) + + case "demangle": + formattingOptions = + formattingOptions.demangle(parseBool(value)) + + case "image-names": + formattingOptions = + formattingOptions.showImageNames(parseBool(value)) + + default: + print(theme.error("unknown option '\(option)'")) + } + + if changedBacktrace { + target.redoBacktraces(limit: args.limit, top: args.top) + } + } + } + } + case "help": + print(""" + Available commands: + + backtrace Display a backtrace. + bt Synonym for backtrace. + debug Attach the debugger. + exit Exit interaction, allowing program to crash normally. + help Display help. + images List images loaded by the program. + mem Synonym for memory. + memory Inspect memory. + process Show information about the process. + quit Synonym for exit. + reg Synonym for registers. + registers Display the registers. + set Set or show options. + thread Show or set the current thread. + threads Synonym for process. + """) + default: + print(theme.error("unknown command '\(cmd[0])'")) + } + + print("") + } + } + + static func printableBytes(from bytes: some Sequence) -> String { + // It would be nice to join these with ZWNJs to prevent ligature processing, + // but sadly Terminal displays ZWNJ as a space character. + return bytes.map{ byte in + switch byte { + case 0..<32, 127, 0x80..<0xa0: + return theme.nonPrintable("·") + default: + return theme.printable(String(Unicode.Scalar(byte))) + } + }.joined(separator:"") + } + + static func dumpMemory(at address: UInt64, count: UInt64) { + guard let bytes = try? target!.reader.fetch( + from: RemoteMemoryReader.Address(address), + count: Int(count), + as: UInt8.self) else { + print("Unable to read memory") + return + } + + let startAddress = HostContext.stripPtrAuth(address: address) + var ndx = 0 + while ndx < bytes.count { + let addr = startAddress + UInt64(ndx) + let remaining = bytes.count - ndx + let lineChunk = 16 + let todo = min(remaining, lineChunk) + let formattedBytes = theme.data(bytes[ndx..(name: String, value: T) { + let hexValue = theme.hexValue(hex(value)) + + // Pad the register name + let regPad = String(repeating: " ", count: max(3 - name.count, 0)) + let reg = theme.register(regPad + name) + + // Grab 16 bytes at each address if possible + if let bytes = try? target!.reader.fetch( + from: RemoteMemoryReader.Address(value), + count: 16, + as: UInt8.self) { + let formattedBytes = theme.data(bytes.map{ + hex($0, withPrefix: false) + }.joined(separator: " ")) + let printedBytes = printableBytes(from: bytes) + print("\(reg) \(hexValue) \(formattedBytes) \(printedBytes)") + } else { + let decValue = theme.decimalValue("\(value)") + print("\(reg) \(hexValue) \(decValue)") + } + } + + static func showGPR(name: String, context: C, register: C.Register) { + // Get the register contents + let value = context.getRegister(register)! + + showRegister(name: name, value: value) + } + + static func showGPRs(_ context: C, range: Rs) where Rs.Element == C.Register { + for reg in range { + showGPR(name: "\(reg)", context: context, register: reg) + } + } + + static func x86StatusFlags(_ flags: T) -> String { + var status: [String] = [] + + if (flags & 0x400) != 0 { + status.append("OF") + } + if (flags & 0x80) != 0 { + status.append("SF") + } + if (flags & 0x40) != 0 { + status.append("ZF") + } + if (flags & 0x10) != 0 { + status.append("AF") + } + if (flags & 0x4) != 0 { + status.append("PF") + } + if (flags & 0x1) != 0 { + status.append("CF") + } + + return status.joined(separator: " ") + } + + static func showRegisters(_ context: X86_64Context) { + showGPRs(context, range: .rax ... .r15) + showRegister(name: "rip", value: context.programCounter) + + let rflags = context.getRegister(.rflags)! + let cs = theme.hexValue(hex(UInt16(context.getRegister(.cs)!))) + let fs = theme.hexValue(hex(UInt16(context.getRegister(.fs)!))) + let gs = theme.hexValue(hex(UInt16(context.getRegister(.gs)!))) + + let hexFlags = theme.hexValue(hex(rflags)) + let status = theme.flags(x86StatusFlags(rflags)) + + print("") + print("\(theme.register("rflags")) \(hexFlags) \(status)") + print("") + print("\(theme.register("cs")) \(cs) \(theme.register("fs")) \(fs) \(theme.register("gs")) \(gs)") + } + + static func showRegisters(_ context: I386Context) { + showGPRs(context, range: .eax ... .edi) + showRegister(name: "eip", value: context.programCounter) + + let eflags = UInt32(context.getRegister(.eflags)!) + let es = theme.hexValue(hex(UInt16(context.getRegister(.es)!))) + let cs = theme.hexValue(hex(UInt16(context.getRegister(.cs)!))) + let ss = theme.hexValue(hex(UInt16(context.getRegister(.ss)!))) + let ds = theme.hexValue(hex(UInt16(context.getRegister(.ds)!))) + let fs = theme.hexValue(hex(UInt16(context.getRegister(.fs)!))) + let gs = theme.hexValue(hex(UInt16(context.getRegister(.gs)!))) + + let hexFlags = theme.hexValue(hex(eflags)) + let status = theme.flags(x86StatusFlags(eflags)) + + print("") + print("\(theme.register("eflags")) \(hexFlags) \(status)") + print("") + print("\(theme.register("es")): \(es) \(theme.register("cs")): \(cs) \(theme.register("ss")): \(ss) \(theme.register("ds")): \(ds) \(theme.register("fs")): \(fs)) \(theme.register("gs")): \(gs)") + } + + static func showRegisters(_ context: ARM64Context) { + showGPRs(context, range: .x0 ..< .x29) + showGPR(name: "fp", context: context, register: .x29) + showGPR(name: "lr", context: context, register: .x30) + showGPR(name: "sp", context: context, register: .sp) + showGPR(name: "pc", context: context, register: .pc) + } + + static func showRegisters(_ context: ARMContext) { + showGPRs(context, range: .r0 ... .r10) + showGPR(name: "fp", context: context, register: .r11) + showGPR(name: "ip", context: context, register: .r12) + showGPR(name: "sp", context: context, register: .r13) + showGPR(name: "lr", context: context, register: .r14) + showGPR(name: "pc", context: context, register: .r15) + } +} + +#else + +@main +internal struct SwiftBacktrace { + static public func main() { + print("swift-backtrace: not supported on this platform.") + } +} + +#endif // os(macOS) diff --git a/test/Backtracing/Crash.swift b/test/Backtracing/Crash.swift new file mode 100644 index 0000000000000..065ddb8f86c34 --- /dev/null +++ b/test/Backtracing/Crash.swift @@ -0,0 +1,178 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -parse-as-library -Onone -g -o %t/Crash +// RUN: %target-build-swift %s -parse-as-library -Onone -o %t/CrashNoDebug +// RUN: %target-build-swift %s -parse-as-library -O -g -o %t/CrashOpt +// RUN: %target-build-swift %s -parse-as-library -O -o %t/CrashOptNoDebug +// RUN: %target-codesign %t/Crash +// RUN: %target-codesign %t/CrashNoDebug +// RUN: %target-codesign %t/CrashOpt +// RUN: %target-codesign %t/CrashOptNoDebug +// RUN: (env SWIFT_BACKTRACE=enable=yes %target-run %t/Crash || true) | %FileCheck %s +// RUN: (env SWIFT_BACKTRACE=preset=friendly,enable=yes %target-run %t/Crash || true) | %FileCheck %s --check-prefix FRIENDLY +// RUN: (env SWIFT_BACKTRACE=enable=yes %target-run %t/CrashNoDebug || true) | %FileCheck %s --check-prefix NODEBUG +// RUN: (env SWIFT_BACKTRACE=enable=yes %target-run %t/CrashOpt || true) | %FileCheck %s --check-prefix OPTIMIZED +// RUN: (env SWIFT_BACKTRACE=enable=yes %target-run %t/CrashOptNoDebug || true) | %FileCheck %s --check-prefix OPTNODEBUG + +// REQUIRES: executable_test +// REQUIRES: OS=macosx + +func level1() { + level2() +} + +func level2() { + level3() +} + +func level3() { + level4() +} + +func level4() { + level5() +} + +func level5() { + print("About to crash") + let ptr = UnsafeMutablePointer(bitPattern: 4)! + ptr.pointee = 42 +} + +@main +struct Crash { + static func main() { + level1() + } +} + +// CHECK: *** Program crashed: Bad pointer dereference at 0x{{0+}}4 *** + +// CHECK: Thread 0 crashed: + +// CHECK: 0 0x{{[0-9a-f]+}} level5() + {{[0-9]+}} in Crash at {{.*}}/Crash.swift:38:15 +// CHECK-NEXT: 1 [ra] 0x{{[0-9a-f]+}} level4() + {{[0-9]+}} in Crash at {{.*}}/Crash.swift:32:3 +// CHECK-NEXT: 2 [ra] 0x{{[0-9a-f]+}} level3() + {{[0-9]+}} in Crash at {{.*}}/Crash.swift:28:3 +// CHECK-NEXT: 3 [ra] 0x{{[0-9a-f]+}} level2() + {{[0-9]+}} in Crash at {{.*}}/Crash.swift:24:3 +// CHECK-NEXT: 4 [ra] 0x{{[0-9a-f]+}} level1() + {{[0-9]+}} in Crash at {{.*}}/Crash.swift:20:3 +// CHECK-NEXT: 5 [ra] 0x{{[0-9a-f]+}} static Crash.main() + {{[0-9]+}} in Crash at {{.*}}/Crash.swift:44:5 +// CHECK-NEXT: 6 [ra] [system] 0x{{[0-9a-f]+}} static Crash.$main() + {{[0-9]+}} in Crash at {{.*}}/Crash.swift:41:1 +// CHECK-NEXT: 7 [ra] [system] 0x{{[0-9a-f]+}} main + {{[0-9]+}} in Crash at {{.*}}/Crash.swift +// CHECK-NEXT: 8 [ra] [system] 0x{{[0-9a-f]+}} start + {{[0-9]+}} in dyld + +// CHECK: Registers: + +// CHECK: Images ({{[0-9]+}} omitted): + +// CHECK: {{0x[0-9a-f]+}}–{{0x[0-9a-f]+}}{{ +}}{{[0-9a-f]+}}{{ +}}Crash{{ +}}{{.*}}/Crash + +// FRIENDLY: *** Program crashed: Bad pointer dereference at 0x{{0+}}4 *** + +// FRIENDLY: Thread 0 crashed: + +// FRIENDLY: 0 level5() + {{[0-9]+}} in Crash at {{.*}}/Crash.swift:38:15 + +// FRIENDLY: 36│ print("About to crash") +// FRIENDLY-NEXT: 37│ let ptr = UnsafeMutablePointer(bitPattern: 4)! +// FRIENDLY-NEXT: 38│ ptr.pointee = 42 +// FRIENDLY-NEXT: │ ▲ +// FRIENDLY-NEXT: 39│ } +// FRIENDLY-NEXT: 40│ + +// FRIENDLY: 1 level4() + {{[0-9]+}} in Crash at {{.*}}/Crash.swift:32:3 + +// FRIENDLY: 30│ +// FRIENDLY-NEXT: 31│ func level4() { +// FRIENDLY-NEXT: 32│ level5() +// FRIENDLY-NEXT: │ ▲ +// FRIENDLY-NEXT: 33│ } +// FRIENDLY-NEXT: 34│ + +// FRIENDLY: 2 level3() + {{[0-9]+}} in Crash at {{.*}}/Crash.swift:28:3 + +// FRIENDLY: 26│ +// FRIENDLY-NEXT: 27│ func level3() { +// FRIENDLY-NEXT: 28│ level4() +// FRIENDLY-NEXT: │ ▲ +// FRIENDLY-NEXT: 29│ } +// FRIENDLY-NEXT: 30│ + +// FRIENDLY: 3 level2() + {{[0-9]+}} in Crash at {{.*}}/Crash.swift:24:3 + +// FRIENDLY: 22│ +// FRIENDLY-NEXT: 23│ func level2() { +// FRIENDLY-NEXT: 24│ level3() +// FRIENDLY-NEXT: │ ▲ +// FRIENDLY-NEXT: 25│ } +// FRIENDLY-NEXT: 26│ + +// FRIENDLY: 4 level1() + {{[0-9]+}} in Crash at {{.*}}/Crash.swift:20:3 + +// FRIENDLY: 18│ +// FRIENDLY-NEXT: 19│ func level1() { +// FRIENDLY-NEXT: 20│ level2() +// FRIENDLY-NEXT: │ ▲ +// FRIENDLY-NEXT: 21│ } +// FRIENDLY-NEXT: 22│ + +// FRIENDLY: 5 static Crash.main() + {{[0-9]+}} in Crash at {{.*}}/Crash.swift:44:5 + +// FRIENDLY: 42│ struct Crash { +// FRIENDLY-NEXT: 43│ static func main() { +// FRIENDLY-NEXT: 44│ level1() +// FRIENDLY-NEXT: │ ▲ +// FRIENDLY-NEXT: 45│ } +// FRIENDLY-NEXT: 46│ } + +// NODEBUG: *** Program crashed: Bad pointer dereference at 0x{{0*}}4 *** + +// NODEBUG: Thread 0 crashed: + +// NODEBUG: 0 0x{{[0-9a-f]+}} level5() + {{[0-9]+}} in CrashNoDebug +// NODEBUG: 1 [ra] 0x{{[0-9a-f]+}} level4() + {{[0-9]+}} in CrashNoDebug +// NODEBUG: 2 [ra] 0x{{[0-9a-f]+}} level3() + {{[0-9]+}} in CrashNoDebug +// NODEBUG: 3 [ra] 0x{{[0-9a-f]+}} level2() + {{[0-9]+}} in CrashNoDebug +// NODEBUG: 4 [ra] 0x{{[0-9a-f]+}} level1() + {{[0-9]+}} in CrashNoDebug +// NODEBUG: 5 [ra] 0x{{[0-9a-f]+}} static Crash.main() + {{[0-9]+}} in CrashNoDebug +// NODEBUG: 6 [ra] [system] 0x{{[0-9a-f]+}} static Crash.$main() + {{[0-9]+}} in CrashNoDebug +// NODEBUG: 7 [ra] 0x{{[0-9a-f]+}} main + {{[0-9]+}} in CrashNoDebug +// NODEBUG: 8 [ra] [system] 0x{{[0-9a-f]+}} start + {{[0-9]+}} in dyld + +// NODEBUG: Registers: + +// NODEBUG: Images ({{[0-9]+}} omitted): + +// NODEBUG: {{0x[0-9a-f]+}}–{{0x[0-9a-f]+}}{{ +}}{{[0-9a-f]+}}{{ +}}CrashNoDebug{{ +}}{{.*}}/CrashNoDebug + +// OPTIMIZED: *** Program crashed: Bad pointer dereference at 0x{{0+}}4 *** + +// OPTIMIZED: Thread 0 crashed: + +// OPTIMIZED: 0 [inlined] 0x{{[0-9a-f]+}} level5() in CrashOpt at {{.*}}/Crash.swift:38:15 +// OPTIMIZED-NEXT: 1 [inlined] 0x{{[0-9a-f]+}} level4() in CrashOpt at {{.*}}/Crash.swift:32:3 +// OPTIMIZED-NEXT: 2 [inlined] 0x{{[0-9a-f]+}} level3() in CrashOpt at {{.*}}/Crash.swift:28:3 +// OPTIMIZED-NEXT: 3 [inlined] 0x{{[0-9a-f]+}} level2() in CrashOpt at {{.*}}/Crash.swift:24:3 +// OPTIMIZED-NEXT: 4 [inlined] 0x{{[0-9a-f]+}} level1() in CrashOpt at {{.*}}/Crash.swift:20:3 +// OPTIMIZED-NEXT: 5 [inlined] 0x{{[0-9a-f]+}} static Crash.main() in CrashOpt at {{.*}}/Crash.swift:44:5 +// OPTIMIZED-NEXT: 6 [inlined] [system] 0x{{[0-9a-f]+}} static Crash.$main() in CrashOpt at {{.*}}/Crash.swift:41:1 +// OPTIMIZED-NEXT: 7 [system] 0x{{[0-9a-f]+}} main + {{[0-9]+}} in CrashOpt at {{.*}}/Crash.swift +// OPTIMIZED-NEXT: 8 [ra] [system] 0x{{[0-9a-f]+}} start + {{[0-9]+}} in dyld + +// OPTIMIZED: Registers: + +// OPTIMIZED: Images ({{[0-9]+}} omitted): + +// OPTIMIZED: {{0x[0-9a-f]+}}–{{0x[0-9a-f]+}}{{ +}}{{[0-9a-f]+}}{{ +}}CrashOpt{{ +}}{{.*}}/CrashOpt + +// OPTNODEBUG: *** Program crashed: Bad pointer dereference at 0x{{0*}}4 *** + +// OPTNODEBUG: Thread 0 crashed: + +// OPTNODEBUG: 0 0x{{[0-9a-f]+}} main + {{[0-9]+}} in CrashOptNoDebug +// OPTNODEBUG-NEXT: 1 [ra] [system] 0x{{[0-9a-f]+}} start + {{[0-9]+}} in dyld + +// OPTNODEBUG: Registers: + +// OPTNODEBUG: Images ({{[0-9]+}} omitted): + +// OPTNODEBUG: {{0x[0-9a-f]+}}–{{0x[0-9a-f]+}}{{ +}}{{[0-9a-f]+}}{{ +}}CrashOptNoDebug{{ +}}{{.*}}/CrashOptNoDebug + diff --git a/test/Backtracing/CrashWithThunk.swift b/test/Backtracing/CrashWithThunk.swift new file mode 100644 index 0000000000000..e518992a4a898 --- /dev/null +++ b/test/Backtracing/CrashWithThunk.swift @@ -0,0 +1,66 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -parse-as-library -Onone -g -o %t/CrashWithThunk +// RUN: %target-codesign %t/CrashWithThunk +// RUN: (env SWIFT_BACKTRACE=enable=yes %target-run %t/CrashWithThunk || true) | %FileCheck %s +// RUN: (env SWIFT_BACKTRACE=preset=friendly,enable=yes %target-run %t/CrashWithThunk || true) | %FileCheck %s --check-prefix FRIENDLY + +// REQUIRES: executable_test +// REQUIRES: OS=macosx + +struct Foo { + var value: T +} + +func crash() { + print("I'm going to crash here") + let ptr = UnsafeMutablePointer(bitPattern: 4)! + ptr.pointee = 42 +} + +@main +struct CrashWithThunk { + static func main() { + let foo = Foo(value: crash) + + foo.value() + } +} + +// CHECK: *** Program crashed: Bad pointer dereference at 0x{{0+}}4 *** + +// CHECK: Thread 0 crashed: + +// CHECK: 0 0x{{[0-9a-f]+}} crash() + {{[0-9]+}} in CrashWithThunk at {{.*}}/CrashWithThunk.swift:17:15 +// CHECK-NEXT: 1 [ra] [thunk] [system] 0x{{[0-9a-f]+}} thunk for @escaping @callee_guaranteed () -> () + {{[0-9]+}} in CrashWithThunk at {{.*}}/Backtracing/ +// CHECK-NEXT: 2 [ra] 0x{{[0-9a-f]+}} static CrashWithThunk.main() + {{[0-9]+}} in CrashWithThunk at {{.*}}/CrashWithThunk.swift:25:9 +// CHECK-NEXT: 3 [ra] [system] 0x{{[0-9a-f]+}} static CrashWithThunk.$main() + {{[0-9]+}} in CrashWithThunk at {{.*}}/CrashWithThunk.swift:20:1 +// CHECK-NEXT: 4 [ra] [system] 0x{{[0-9a-f]+}} main + {{[0-9]+}} in CrashWithThunk at {{.*}}/CrashWithThunk.swift +// CHECK-NEXT: 5 [ra] [system] 0x{{[0-9a-f]+}} start + {{[0-9]+}} in dyld + +// CHECK: Registers: + +// CHECK: Images ({{[0-9]+}} omitted): + +// CHECK: {{0x[0-9a-f]+}}–{{0x[0-9a-f]+}}{{ +}}{{[0-9a-f]+}}{{ +}}CrashWithThunk{{ +}}{{.*}}/CrashWithThunk + +// FRIENDLY: *** Program crashed: Bad pointer dereference at 0x{{0+}}4 *** + +// FRIENDLY: Thread 0 crashed: + +// FRIENDLY: 0 crash() + {{[0-9]+}} in CrashWithThunk at {{.*}}/CrashWithThunk.swift:17:15 + +// FRIENDLY: 15│ print("I'm going to crash here") +// FRIENDLY-NEXT: 16│ let ptr = UnsafeMutablePointer(bitPattern: 4)! +// FRIENDLY-NEXT: 17│ ptr.pointee = 42 +// FRIENDLY-NEXT: │ ▲ +// FRIENDLY-NEXT: 18│ } +// FRIENDLY-NEXT: 19│ + +// FRIENDLY: 1 static CrashWithThunk.main() + {{[0-9]+}} in CrashWithThunk at {{.*}}/CrashWithThunk.swift:25:9 + +// FRIENDLY: 23│ let foo = Foo(value: crash) +// FRIENDLY-NEXT: 24│ +// FRIENDLY-NEXT: 25│ foo.value() +// FRIENDLY-NEXT: │ ▲ +// FRIENDLY-NEXT: 26│ } +// FRIENDLY-NEXT: 27│ } diff --git a/test/Backtracing/Overflow.swift b/test/Backtracing/Overflow.swift new file mode 100644 index 0000000000000..4decb083cb8ef --- /dev/null +++ b/test/Backtracing/Overflow.swift @@ -0,0 +1,110 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -parse-as-library -Onone -g -o %t/Overflow +// RUN: %target-codesign %t/Overflow +// RUN: (env SWIFT_BACKTRACE=enable=yes %target-run %t/Overflow || true) | %FileCheck %s +// RUN: (env SWIFT_BACKTRACE=preset=friendly,enable=yes %target-run %t/Overflow || true) | %FileCheck %s --check-prefix FRIENDLY + +// REQUIRES: executable_test +// REQUIRES: OS=macosx +var x: UInt = 0 + +func level1() { + level2() +} + +func level2() { + level3() +} + +func level3() { + level4() +} + +func level4() { + level5() +} + +func level5() { + print("About to overflow") + + x -= 1 +} + +@main +struct Overflow { + static func main() { + level1() + } +} + +// CHECK: *** Swift runtime failure: arithmetic overflow *** + +// CHECK: Thread 0 crashed: + +// CHECK: 0 [inlined] [system] 0x{{[0-9a-f]+}} Swift runtime failure: arithmetic overflow in Overflow at {{.*}}/ +// CHECK-NEXT: 1 0x{{[0-9a-f]+}} level5() + {{[0-9]+}} in Overflow at {{.*}}/Overflow.swift:{{30|29}}:5 +// CHECK-NEXT: 2 [ra] 0x{{[0-9a-f]+}} level4() + {{[0-9]+}} in Overflow at {{.*}}/Overflow.swift:{{24|23}}:3 +// CHECK-NEXT: 3 [ra] 0x{{[0-9a-f]+}} level3() + {{[0-9]+}} in Overflow at {{.*}}/Overflow.swift:{{20|19}}:3 +// CHECK-NEXT: 4 [ra] 0x{{[0-9a-f]+}} level2() + {{[0-9]+}} in Overflow at {{.*}}/Overflow.swift:{{16|15}}:3 +// CHECK-NEXT: 5 [ra] 0x{{[0-9a-f]+}} level1() + {{[0-9]+}} in Overflow at {{.*}}/Overflow.swift:{{12|11}}:3 +// CHECK-NEXT: 6 [ra] 0x{{[0-9a-f]+}} static Overflow.main() + {{[0-9]+}} in Overflow at {{.*}}/Overflow.swift:36:5 +// CHECK-NEXT: 7 [ra] [system] 0x{{[0-9a-f]+}} static Overflow.$main() + {{[0-9]+}} in Overflow at {{.*}}/Overflow.swift:33:1 +// CHECK-NEXT: 8 [ra] [system] 0x{{[0-9a-f]+}} main + {{[0-9]+}} in Overflow at {{.*}}/Overflow.swift +// CHECK-NEXT: 9 [ra] [system] 0x{{[0-9a-f]+}} start + {{[0-9]+}} in dyld + +// CHECK: Registers: + +// CHECK: Images ({{[0-9]+}} omitted): + +// CHECK: {{0x[0-9a-f]+}}–{{0x[0-9a-f]+}}{{ +}}{{[0-9a-f]+}}{{ +}}Overflow{{ +}}{{.*}}/Overflow + +// FRIENDLY: *** Swift runtime failure: arithmetic overflow *** + +// FRIENDLY: Thread 0 crashed: + +// FRIENDLY: 0 level5() + {{[0-9]+}} in Overflow at {{.*}}/Overflow.swift:30:5 + +// FRIENDLY: 28│ print("About to overflow") +// FRIENDLY-NEXT: 29│ +// FRIENDLY-NEXT: 30│ x -= 1 +// FRIENDLY-NEXT: │ ▲ +// FRIENDLY-NEXT: 31│ } + +// FRIENDLY: 1 level4() + {{[0-9]+}} in Overflow at {{.*}}/Overflow.swift:24:3 + +// FRIENDLY: 22│ +// FRIENDLY-NEXT: 23│ func level4() { +// FRIENDLY-NEXT: 24│ level5() +// FRIENDLY-NEXT: │ ▲ +// FRIENDLY-NEXT: 25│ } +// FRIENDLY-NEXT: 26│ + +// FRIENDLY: 2 level3() + {{[0-9]+}} in Overflow at {{.*}}/Overflow.swift:20:3 + +// FRIENDLY: 18│ +// FRIENDLY-NEXT: 19│ func level3() { +// FRIENDLY-NEXT: 20│ level4() +// FRIENDLY-NEXT: │ ▲ +// FRIENDLY-NEXT: 21│ } +// FRIENDLY-NEXT: 22│ + +// FRIENDLY: 3 level2() + {{[0-9]+}} in Overflow at {{.*}}/Overflow.swift:16:3 + +// FRIENDLY: 14│ +// FRIENDLY-NEXT: 15│ func level2() { +// FRIENDLY-NEXT: 16│ level3() +// FRIENDLY-NEXT: │ ▲ +// FRIENDLY-NEXT: 17│ } +// FRIENDLY-NEXT: 18│ + +// FRIENDLY: 4 level1() + {{[0-9]+}} in Overflow at {{.*}}/Overflow.swift:12:3 + +// FRIENDLY: 10│ +// FRIENDLY-NEXT: 11│ func level1() { +// FRIENDLY-NEXT: 12│ level2() +// FRIENDLY-NEXT: │ ▲ +// FRIENDLY-NEXT: 13│ } +// FRIENDLY-NEXT: 14│ + +// FRIENDLY: 5 static Overflow.main() + {{[0-9]+}} in Overflow at {{.*}}/Overflow.swift + diff --git a/test/Backtracing/StackOverflow.swift b/test/Backtracing/StackOverflow.swift new file mode 100644 index 0000000000000..e35b8d7b4bc2f --- /dev/null +++ b/test/Backtracing/StackOverflow.swift @@ -0,0 +1,212 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -parse-as-library -Onone -g -o %t/StackOverflow +// RUN: %target-codesign %t/StackOverflow +// RUN: (env SWIFT_BACKTRACE=enable=yes %target-run %t/StackOverflow || true) | %FileCheck %s +// RUN: (env SWIFT_BACKTRACE=limit=16,top=4,enable=yes %target-run %t/StackOverflow || true) | %FileCheck %s --check-prefix LIMITED +// RUN: (env SWIFT_BACKTRACE=preset=friendly,enable=yes %target-run %t/StackOverflow || true) | %FileCheck %s --check-prefix FRIENDLY + +// REQUIRES: executable_test +// REQUIRES: OS=macosx + +func recurse(_ level: Int) { + if level % 100000 == 0 { + print(level) + } + recurse(level + 1) +} + +@main +struct StackOverflow { + static func main() { + recurse(1) + } +} + +// FIXME: We have to allow all the line numbers below to be off-by-one because +// of a CoreSymbolication bug. This also means we have to skip checking the +// source code output because currently, it's pointing at the wrong line :-( + +// CHECK: *** Program crashed: Bad pointer dereference at 0x{{[0-9a-f]+}} *** + +// CHECK: Thread 0 crashed: + +// CHECK: 0 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{[0-9]+}} +// CHECK-NEXT: 1 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 2 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 3 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 4 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 5 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 6 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 7 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 8 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 9 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 10 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 11 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 12 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 13 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 14 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 15 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 16 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 17 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 18 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 19 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 20 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 21 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 22 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 23 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 24 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 25 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 26 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 27 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 28 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 29 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 30 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 31 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 32 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 33 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 34 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 35 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 36 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 37 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 38 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 39 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 40 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 41 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 42 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 43 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 44 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 45 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 46 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: ... +// CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} static StackOverflow.main() + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{21|20}}:5 +// CHECK-NEXT: {{[0-9]+}} [ra] [system] 0x{{[0-9a-f]+}} static StackOverflow.$main() + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{18|17}}:1 +// CHECK-NEXT: {{[0-9]+}} [ra] [system] 0x{{[0-9a-f]+}} main + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift +// CHECK-NEXT: {{[0-9]+}} [ra] [system] 0x{{[0-9a-f]+}} start + {{[0-9]+}} in dyld + +// CHECK: Registers: + +// CHECK: Images ({{[0-9]+}} omitted): + +// CHECK: {{0x[0-9a-f]+}}–{{0x[0-9a-f]+}}{{ +}}{{[0-9a-f]+}}{{ +}}StackOverflow{{ +}}{{.*}}/StackOverflow + +// LIMITED: *** Program crashed: Bad pointer dereference at 0x{{[0-9a-f]+}} *** + +// LIMITED: Thread 0 crashed: + +// LIMITED: 0 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{[0-9]+}} +// LIMITED-NEXT: 1 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// LIMITED-NEXT: 2 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// LIMITED-NEXT: 3 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// LIMITED-NEXT: 4 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// LIMITED-NEXT: 5 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// LIMITED-NEXT: 6 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// LIMITED-NEXT: 7 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// LIMITED-NEXT: 8 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// LIMITED-NEXT: 9 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// LIMITED-NEXT: 10 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// LIMITED-NEXT: ... +// LIMITED-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} static StackOverflow.main() + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{21|20}}:5 +// LIMITED-NEXT: {{[0-9]+}} [ra] [system] 0x{{[0-9a-f]+}} static StackOverflow.$main() + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{18|17}}:1 +// LIMITED-NEXT: {{[0-9]+}} [ra] [system] 0x{{[0-9a-f]+}} main + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift +// LIMITED-NEXT: {{[0-9]+}} [ra] [system] 0x{{[0-9a-f]+}} start + {{[0-9]+}} in dyld + +// FRIENDLY: *** Program crashed: Bad pointer dereference at 0x{{[0-9a-f]+}} *** + +// FRIENDLY: Thread 0 crashed: + +// FRIENDLY: 0 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{[0-9]+}} + +// SKIP-FRIENDLY: 8│ // REQUIRES: executable_test +// SKIP-FRIENDLY-NEXT: 9│ // REQUIRES: OS=macosx +// SKIP-FRIENDLY-NEXT: 10│ +// SKIP-FRIENDLY-NEXT: 11│ func recurse(_ level: Int) { +// SKIP-FRIENDLY-NEXT: │ ▲ +// SKIP-FRIENDLY-NEXT: 12│ if level % 100000 == 0 { + +// FRIENDLY: 1 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 + +// SKIP-FRIENDLY: 12│ if level % 100000 == 0 { +// SKIP-FRIENDLY-NEXT: 13│ print(level) +// SKIP-FRIENDLY-NEXT: 14│ } +// SKIP-FRIENDLY-NEXT: 15│ recurse(level + 1) +// SKIP-FRIENDLY-NEXT: │ ▲ +// SKIP-FRIENDLY-NEXT: 16│ } + +// FRIENDLY: 2 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: 3 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: 4 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: 5 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: 6 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: 7 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: 8 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: 9 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: 10 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: 11 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: 12 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: 13 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: 14 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: 15 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: 16 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: 17 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: 18 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: 19 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: 20 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: 21 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: 22 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: 23 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: 24 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: 25 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: 26 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: 27 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: 28 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: 29 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: 30 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: 31 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: 32 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: 33 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: 34 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: 35 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: 36 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: 37 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: 38 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: 39 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: 40 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: 41 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: 42 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: 43 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: 44 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: 45 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: 46 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: ... +// FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: {{[0-9]+}} static StackOverflow.main() + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{21|20}}:5 + +// SKIP-FRIENDLY: 18│ @main +// SKIP-FRIENDLY-NEXT: 19│ struct StackOverflow { +// SKIP-FRIENDLY-NEXT: 20│ static func main() { +// SKIP-FRIENDLY-NEXT: 21│ recurse(1) +// SKIP-FRIENDLY-NEXT: │ ▲ +// SKIP-FRIENDLY-NEXT: 22│ } diff --git a/test/Backtracing/SymbolicatedBacktrace.swift b/test/Backtracing/SymbolicatedBacktrace.swift new file mode 100644 index 0000000000000..23a692f93032b --- /dev/null +++ b/test/Backtracing/SymbolicatedBacktrace.swift @@ -0,0 +1,50 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -parse-as-library -g -Onone -o %t/SymbolicatedBacktrace +// RUN: %target-codesign %t/SymbolicatedBacktrace +// RUN: %target-run %t/SymbolicatedBacktrace | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: OS=macosx || OS=ios || OS=watchOS || OS=tvOS + +import _Backtracing + +func kablam() { + kerpow() +} + +func kerpow() { + whap() +} + +func whap() { + zonk() +} + +func zonk() { + splat() +} + +func splat() { + pow() +} + +func pow() { + let backtrace = try! Backtrace.capture().symbolicated()! + + // CHECK: 0{{[ \t]+}}0x{{[0-9a-f]+}} [ra] [0] SymbolicatedBacktrace pow() + // CHECK: 1{{[ \t]+}}0x{{[0-9a-f]+}} [ra] [0] SymbolicatedBacktrace splat() + // CHECK: 2{{[ \t]+}}0x{{[0-9a-f]+}} [ra] [0] SymbolicatedBacktrace zonk() + // CHECK: 3{{[ \t]+}}0x{{[0-9a-f]+}} [ra] [0] SymbolicatedBacktrace whap() + // CHECK: 4{{[ \t]+}}0x{{[0-9a-f]+}} [ra] [0] SymbolicatedBacktrace kerpow() + // CHECK: 5{{[ \t]+}}0x{{[0-9a-f]+}} [ra] [0] SymbolicatedBacktrace kablam() + // CHECK: 6{{[ \t]+}}0x{{[0-9a-f]+}} [ra] [0] SymbolicatedBacktrace static SymbolicatedBacktrace.main() + + print(backtrace) +} + +@main +struct SymbolicatedBacktrace { + static func main() { + kablam() + } +} diff --git a/test/Backtracing/SymbolicatedBacktraceInline.swift b/test/Backtracing/SymbolicatedBacktraceInline.swift new file mode 100644 index 0000000000000..c8701669fb8b2 --- /dev/null +++ b/test/Backtracing/SymbolicatedBacktraceInline.swift @@ -0,0 +1,50 @@ +// RUN: %empty-directory(%t) +// RUN: %target-build-swift %s -parse-as-library -g -O -o %t/SymbolicatedBacktraceInline +// RUN: %target-codesign %t/SymbolicatedBacktraceInline +// RUN: %target-run %t/SymbolicatedBacktraceInline | %FileCheck %s + +// REQUIRES: executable_test +// REQUIRES: OS=macosx || OS=ios || OS=watchOS || OS=tvOS + +import _Backtracing + +func kablam() { + kerpow() +} + +func kerpow() { + whap() +} + +func whap() { + zonk() +} + +func zonk() { + splat() +} + +func splat() { + pow() +} + +func pow() { + let backtrace = try! Backtrace.capture().symbolicated()! + + // CHECK: 0{{[ \t]+}}0x{{[0-9a-f]+}} [ra] [0] SymbolicatedBacktraceInline pow() + // CHECK: 1{{[ \t]+}}0x{{[0-9a-f]+}} [ra] [inlined] [0] SymbolicatedBacktraceInline splat() + // CHECK: 2{{[ \t]+}}0x{{[0-9a-f]+}} [ra] [inlined] [0] SymbolicatedBacktraceInline zonk() + // CHECK: 3{{[ \t]+}}0x{{[0-9a-f]+}} [ra] [inlined] [0] SymbolicatedBacktraceInline whap() + // CHECK: 4{{[ \t]+}}0x{{[0-9a-f]+}} [ra] [inlined] [0] SymbolicatedBacktraceInline kerpow() + // CHECK: 5{{[ \t]+}}0x{{[0-9a-f]+}} [ra] [inlined] [0] SymbolicatedBacktraceInline kablam() + // CHECK: 6{{[ \t]+}}0x{{[0-9a-f]+}} [ra] [inlined] [0] SymbolicatedBacktraceInline static SymbolicatedBacktraceInline.main() + + print(backtrace) +} + +@main +struct SymbolicatedBacktraceInline { + static func main() { + kablam() + } +} From 2377998ed76c2bf7ecb7699fb724e3bea0ba95d5 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Mon, 20 Feb 2023 18:44:27 +0000 Subject: [PATCH 18/22] [Backtracing] Tweak output slightly. Fixed the colours so that they work with all of the default Terminal presets. Also changed things so that when colour is off, we only use ASCII characters in our source code displays. rdar://105452194 --- .../Backtracing/BacktraceFormatter.swift | 28 +++++--- .../libexec/swift-backtrace/Themes.swift | 11 +-- test/Backtracing/Crash.swift | 72 +++++++++---------- test/Backtracing/CrashWithThunk.swift | 24 +++---- test/Backtracing/Overflow.swift | 58 +++++++-------- 5 files changed, 101 insertions(+), 92 deletions(-) diff --git a/stdlib/public/Backtracing/BacktraceFormatter.swift b/stdlib/public/Backtracing/BacktraceFormatter.swift index bf4be723d44f3..80672e5d7d40b 100644 --- a/stdlib/public/Backtracing/BacktraceFormatter.swift +++ b/stdlib/public/Backtracing/BacktraceFormatter.swift @@ -38,8 +38,9 @@ public protocol BacktraceFormattingTheme { func sourceLocation(_ s: String) -> String func lineNumber(_ s: String) -> String func code(_ s: String) -> String + func crashedLineNumber(_ s: String) -> String func crashedLine(_ s: String) -> String - func crashLocation(_ s: String) -> String + func crashLocation() -> String func imageName(_ s: String) -> String func imageAddressRange(_ s: String) -> String func imageBuildID(_ s: String) -> String @@ -53,10 +54,11 @@ extension BacktraceFormattingTheme { public func symbol(_ s: String) -> String { return s } public func offset(_ s: String) -> String { return s } public func sourceLocation(_ s: String) -> String { return s } - public func lineNumber(_ s: String) -> String { return s } + public func lineNumber(_ s: String) -> String { return " \(s)|" } public func code(_ s: String) -> String { return s } + public func crashedLineNumber(_ s: String) -> String { return "*\(s)|" } public func crashedLine(_ s: String) -> String { return s } - public func crashLocation(_ s: String) -> String { return s } + public func crashLocation() -> String { return "^" } public func imageName(_ s: String) -> String { return s } public func imageAddressRange(_ s: String) -> String { return s } public func imageBuildID(_ s: String) -> String { return s } @@ -638,16 +640,19 @@ public struct BacktraceFormatter { && line <= sourceLocation.line + options._sourceContextLines { let untabified = untabify(sourceLine) let code = options._theme.code(untabified) - let lineNumber = options._theme.lineNumber(pad("\(line)", - to: maxLineWidth, - aligned: .right)) let theLine: String if line == sourceLocation.line { + let lineNumber = options._theme.crashedLineNumber(pad("\(line)", + to: maxLineWidth, + aligned: .right)) let highlightWidth = options._width - 2 * theIndent - theLine = options._theme.crashedLine(pad("\(lineNumber)│ \(code) ", + theLine = options._theme.crashedLine(pad("\(lineNumber) \(code)", to: highlightWidth)) } else { - theLine = "\(lineNumber)│ \(code)" + let lineNumber = options._theme.lineNumber(pad("\(line)", + to: maxLineWidth, + aligned: .right)) + theLine = "\(lineNumber) \(code)" } lines.append("\(indent)\(theLine)") @@ -676,10 +681,11 @@ public struct BacktraceFormatter { let pad = String(repeating: " ", count: max(terminalWidth - 1, 0)) - let marker = options._theme.crashLocation("▲") - let blankForNumber = String(repeating: " ", count: maxLineWidth) + let marker = options._theme.crashLocation() + let blankForNumber = options._theme.lineNumber( + String(repeating: " ", count: maxLineWidth)) - lines.append("\(indent)\(blankForNumber)│ \(pad)\(marker)") + lines.append("\(indent)\(blankForNumber) \(pad)\(marker)") } } } diff --git a/stdlib/public/libexec/swift-backtrace/Themes.swift b/stdlib/public/libexec/swift-backtrace/Themes.swift index 1dc6f88da1a29..f9addbc90287f 100644 --- a/stdlib/public/libexec/swift-backtrace/Themes.swift +++ b/stdlib/public/libexec/swift-backtrace/Themes.swift @@ -95,16 +95,19 @@ enum Themes { return "\(fg: .yellow)\(s)\(fg: .normal)" } public func lineNumber(_ s: String) -> String { - return "\(fg: .gray)\(s)\(fg: .normal)" + return "\(fg: .gray)\(s)\(fg: .normal)│" } public func code(_ s: String) -> String { return "\(s)" } + public func crashedLineNumber(_ s: String) -> String { + return "\(fg: .gray)\(s)\(fg: .brightWhite)│" + } public func crashedLine(_ s: String) -> String { - return "\(bg: .grayscale(2))\(s)\(bg: .normal)" + return "\(bg: .grayscale(2))\(fg: .brightWhite)\(s)\(fg: .normal)\(bg: .normal)" } - public func crashLocation(_ s: String) -> String { - return "\(fg: .brightRed)\(s)\(fg: .normal)" + public func crashLocation() -> String { + return "\(fg: .brightRed)▲\(fg: .normal)" } public func imageName(_ s: String) -> String { return "\(fg: .cyan)\(s)\(fg: .normal)" diff --git a/test/Backtracing/Crash.swift b/test/Backtracing/Crash.swift index 065ddb8f86c34..2528049d923ad 100644 --- a/test/Backtracing/Crash.swift +++ b/test/Backtracing/Crash.swift @@ -71,57 +71,57 @@ struct Crash { // FRIENDLY: 0 level5() + {{[0-9]+}} in Crash at {{.*}}/Crash.swift:38:15 -// FRIENDLY: 36│ print("About to crash") -// FRIENDLY-NEXT: 37│ let ptr = UnsafeMutablePointer(bitPattern: 4)! -// FRIENDLY-NEXT: 38│ ptr.pointee = 42 -// FRIENDLY-NEXT: │ ▲ -// FRIENDLY-NEXT: 39│ } -// FRIENDLY-NEXT: 40│ +// FRIENDLY: 36| print("About to crash") +// FRIENDLY-NEXT: 37| let ptr = UnsafeMutablePointer(bitPattern: 4)! +// FRIENDLY-NEXT: 38| ptr.pointee = 42 +// FRIENDLY-NEXT: | ^ +// FRIENDLY-NEXT: 39| } +// FRIENDLY-NEXT: 40| // FRIENDLY: 1 level4() + {{[0-9]+}} in Crash at {{.*}}/Crash.swift:32:3 -// FRIENDLY: 30│ -// FRIENDLY-NEXT: 31│ func level4() { -// FRIENDLY-NEXT: 32│ level5() -// FRIENDLY-NEXT: │ ▲ -// FRIENDLY-NEXT: 33│ } -// FRIENDLY-NEXT: 34│ +// FRIENDLY: 30| +// FRIENDLY-NEXT: 31| func level4() { +// FRIENDLY-NEXT: 32| level5() +// FRIENDLY-NEXT: | ^ +// FRIENDLY-NEXT: 33| } +// FRIENDLY-NEXT: 34| // FRIENDLY: 2 level3() + {{[0-9]+}} in Crash at {{.*}}/Crash.swift:28:3 -// FRIENDLY: 26│ -// FRIENDLY-NEXT: 27│ func level3() { -// FRIENDLY-NEXT: 28│ level4() -// FRIENDLY-NEXT: │ ▲ -// FRIENDLY-NEXT: 29│ } -// FRIENDLY-NEXT: 30│ +// FRIENDLY: 26| +// FRIENDLY-NEXT: 27| func level3() { +// FRIENDLY-NEXT: 28| level4() +// FRIENDLY-NEXT: | ^ +// FRIENDLY-NEXT: 29| } +// FRIENDLY-NEXT: 30| // FRIENDLY: 3 level2() + {{[0-9]+}} in Crash at {{.*}}/Crash.swift:24:3 -// FRIENDLY: 22│ -// FRIENDLY-NEXT: 23│ func level2() { -// FRIENDLY-NEXT: 24│ level3() -// FRIENDLY-NEXT: │ ▲ -// FRIENDLY-NEXT: 25│ } -// FRIENDLY-NEXT: 26│ +// FRIENDLY: 22| +// FRIENDLY-NEXT: 23| func level2() { +// FRIENDLY-NEXT: 24| level3() +// FRIENDLY-NEXT: | ^ +// FRIENDLY-NEXT: 25| } +// FRIENDLY-NEXT: 26| // FRIENDLY: 4 level1() + {{[0-9]+}} in Crash at {{.*}}/Crash.swift:20:3 -// FRIENDLY: 18│ -// FRIENDLY-NEXT: 19│ func level1() { -// FRIENDLY-NEXT: 20│ level2() -// FRIENDLY-NEXT: │ ▲ -// FRIENDLY-NEXT: 21│ } -// FRIENDLY-NEXT: 22│ +// FRIENDLY: 18| +// FRIENDLY-NEXT: 19| func level1() { +// FRIENDLY-NEXT: 20| level2() +// FRIENDLY-NEXT: | ^ +// FRIENDLY-NEXT: 21| } +// FRIENDLY-NEXT: 22| // FRIENDLY: 5 static Crash.main() + {{[0-9]+}} in Crash at {{.*}}/Crash.swift:44:5 -// FRIENDLY: 42│ struct Crash { -// FRIENDLY-NEXT: 43│ static func main() { -// FRIENDLY-NEXT: 44│ level1() -// FRIENDLY-NEXT: │ ▲ -// FRIENDLY-NEXT: 45│ } -// FRIENDLY-NEXT: 46│ } +// FRIENDLY: 42| struct Crash { +// FRIENDLY-NEXT: 43| static func main() { +// FRIENDLY-NEXT: 44| level1() +// FRIENDLY-NEXT: | ^ +// FRIENDLY-NEXT: 45| } +// FRIENDLY-NEXT: 46| } // NODEBUG: *** Program crashed: Bad pointer dereference at 0x{{0*}}4 *** diff --git a/test/Backtracing/CrashWithThunk.swift b/test/Backtracing/CrashWithThunk.swift index e518992a4a898..f77ddbc89f354 100644 --- a/test/Backtracing/CrashWithThunk.swift +++ b/test/Backtracing/CrashWithThunk.swift @@ -49,18 +49,18 @@ struct CrashWithThunk { // FRIENDLY: 0 crash() + {{[0-9]+}} in CrashWithThunk at {{.*}}/CrashWithThunk.swift:17:15 -// FRIENDLY: 15│ print("I'm going to crash here") -// FRIENDLY-NEXT: 16│ let ptr = UnsafeMutablePointer(bitPattern: 4)! -// FRIENDLY-NEXT: 17│ ptr.pointee = 42 -// FRIENDLY-NEXT: │ ▲ -// FRIENDLY-NEXT: 18│ } -// FRIENDLY-NEXT: 19│ +// FRIENDLY: 15| print("I'm going to crash here") +// FRIENDLY-NEXT: 16| let ptr = UnsafeMutablePointer(bitPattern: 4)! +// FRIENDLY-NEXT: 17| ptr.pointee = 42 +// FRIENDLY-NEXT: | ^ +// FRIENDLY-NEXT: 18| } +// FRIENDLY-NEXT: 19| // FRIENDLY: 1 static CrashWithThunk.main() + {{[0-9]+}} in CrashWithThunk at {{.*}}/CrashWithThunk.swift:25:9 -// FRIENDLY: 23│ let foo = Foo(value: crash) -// FRIENDLY-NEXT: 24│ -// FRIENDLY-NEXT: 25│ foo.value() -// FRIENDLY-NEXT: │ ▲ -// FRIENDLY-NEXT: 26│ } -// FRIENDLY-NEXT: 27│ } +// FRIENDLY: 23| let foo = Foo(value: crash) +// FRIENDLY-NEXT: 24| +// FRIENDLY-NEXT: 25| foo.value() +// FRIENDLY-NEXT: | ^ +// FRIENDLY-NEXT: 26| } +// FRIENDLY-NEXT: 27| } diff --git a/test/Backtracing/Overflow.swift b/test/Backtracing/Overflow.swift index 4decb083cb8ef..60d41c85ed086 100644 --- a/test/Backtracing/Overflow.swift +++ b/test/Backtracing/Overflow.swift @@ -64,47 +64,47 @@ struct Overflow { // FRIENDLY: 0 level5() + {{[0-9]+}} in Overflow at {{.*}}/Overflow.swift:30:5 -// FRIENDLY: 28│ print("About to overflow") -// FRIENDLY-NEXT: 29│ -// FRIENDLY-NEXT: 30│ x -= 1 -// FRIENDLY-NEXT: │ ▲ -// FRIENDLY-NEXT: 31│ } +// FRIENDLY: 28| print("About to overflow") +// FRIENDLY-NEXT: 29| +// FRIENDLY-NEXT: 30| x -= 1 +// FRIENDLY-NEXT: | ^ +// FRIENDLY-NEXT: 31| } // FRIENDLY: 1 level4() + {{[0-9]+}} in Overflow at {{.*}}/Overflow.swift:24:3 -// FRIENDLY: 22│ -// FRIENDLY-NEXT: 23│ func level4() { -// FRIENDLY-NEXT: 24│ level5() -// FRIENDLY-NEXT: │ ▲ -// FRIENDLY-NEXT: 25│ } -// FRIENDLY-NEXT: 26│ +// FRIENDLY: 22| +// FRIENDLY-NEXT: 23| func level4() { +// FRIENDLY-NEXT: 24| level5() +// FRIENDLY-NEXT: | ^ +// FRIENDLY-NEXT: 25| } +// FRIENDLY-NEXT: 26| // FRIENDLY: 2 level3() + {{[0-9]+}} in Overflow at {{.*}}/Overflow.swift:20:3 -// FRIENDLY: 18│ -// FRIENDLY-NEXT: 19│ func level3() { -// FRIENDLY-NEXT: 20│ level4() -// FRIENDLY-NEXT: │ ▲ -// FRIENDLY-NEXT: 21│ } -// FRIENDLY-NEXT: 22│ +// FRIENDLY: 18| +// FRIENDLY-NEXT: 19| func level3() { +// FRIENDLY-NEXT: 20| level4() +// FRIENDLY-NEXT: | ^ +// FRIENDLY-NEXT: 21| } +// FRIENDLY-NEXT: 22| // FRIENDLY: 3 level2() + {{[0-9]+}} in Overflow at {{.*}}/Overflow.swift:16:3 -// FRIENDLY: 14│ -// FRIENDLY-NEXT: 15│ func level2() { -// FRIENDLY-NEXT: 16│ level3() -// FRIENDLY-NEXT: │ ▲ -// FRIENDLY-NEXT: 17│ } -// FRIENDLY-NEXT: 18│ +// FRIENDLY: 14| +// FRIENDLY-NEXT: 15| func level2() { +// FRIENDLY-NEXT: 16| level3() +// FRIENDLY-NEXT: | ^ +// FRIENDLY-NEXT: 17| } +// FRIENDLY-NEXT: 18| // FRIENDLY: 4 level1() + {{[0-9]+}} in Overflow at {{.*}}/Overflow.swift:12:3 -// FRIENDLY: 10│ -// FRIENDLY-NEXT: 11│ func level1() { -// FRIENDLY-NEXT: 12│ level2() -// FRIENDLY-NEXT: │ ▲ -// FRIENDLY-NEXT: 13│ } -// FRIENDLY-NEXT: 14│ +// FRIENDLY: 10| +// FRIENDLY-NEXT: 11| func level1() { +// FRIENDLY-NEXT: 12| level2() +// FRIENDLY-NEXT: | ^ +// FRIENDLY-NEXT: 13| } +// FRIENDLY-NEXT: 14| // FRIENDLY: 5 static Overflow.main() + {{[0-9]+}} in Overflow at {{.*}}/Overflow.swift From fd8845f6c2db70a60b30045fe9c06bd19f0b16b5 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Thu, 2 Mar 2023 15:37:25 +0000 Subject: [PATCH 19/22] [Backtracing][Windows] Use CRT not MSVCRT. The name of the C library on Windows is CRT, apparently. rdar://105452194 --- stdlib/public/libexec/swift-backtrace/Utils.swift | 4 ++-- stdlib/public/libexec/swift-backtrace/main.swift | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/stdlib/public/libexec/swift-backtrace/Utils.swift b/stdlib/public/libexec/swift-backtrace/Utils.swift index be28d98e0c545..d0923ffca14c9 100644 --- a/stdlib/public/libexec/swift-backtrace/Utils.swift +++ b/stdlib/public/libexec/swift-backtrace/Utils.swift @@ -18,8 +18,8 @@ import Darwin.C #elseif canImport(Glibc) import Glibc -#elseif canImport(MSVCRT) -import MSVCRT +#elseif canImport(CRT) +import CRT #endif import Swift diff --git a/stdlib/public/libexec/swift-backtrace/main.swift b/stdlib/public/libexec/swift-backtrace/main.swift index ddde90f23e78e..2e9fa95e57ccd 100644 --- a/stdlib/public/libexec/swift-backtrace/main.swift +++ b/stdlib/public/libexec/swift-backtrace/main.swift @@ -16,8 +16,8 @@ import Darwin.C #elseif canImport(Glibc) import Glibc -#elseif canImport(MSVCRT) -import MSVCRT +#elseif canImport(CRT) +import CRT #endif @_spi(Formatting) import _Backtracing From 46298b9f59d5fa2df4a20a9a79580cbc3dfdaf68 Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Mon, 13 Feb 2023 15:39:34 +0000 Subject: [PATCH 20/22] [Backtracing][Docs] Add an explanation of the workings of the backtracer. Add some discussion of how the new external backtracer works and what options are available. rdar://105394365 --- docs/Backtracing.rst | 169 +++++++++++++++++++++++++++++++++++++++++++ docs/README.md | 3 + 2 files changed, 172 insertions(+) create mode 100644 docs/Backtracing.rst diff --git a/docs/Backtracing.rst b/docs/Backtracing.rst new file mode 100644 index 0000000000000..2c4eb9a3adbb6 --- /dev/null +++ b/docs/Backtracing.rst @@ -0,0 +1,169 @@ +Backtracing support in Swift +============================ + +When things go wrong, it's always useful to be able to get a backtrace showing +where the problem occurred in your program. + +Broadly speaking there are three circumstances where you might want a backtrace, +namely: + + * Program crashes + * Runtime errors + * Specific user-defined program events + +Historically, Swift has tended to lean on operating system crash catching +support for the first two of these, and hasn't really provided any built-in +support for the latter. This is fine for Darwin, where the operating system +provides a comprehensive system-wide crash catching facility; it's just about OK +on Windows, which also has system-wide crash logging; but it isn't great +elsewhere, in particular on Linux where a lot of server-side Swift programs +currently rely on a separate package to provide them with some level of +backtrace support when errors happen. + +What does Swift now support? +---------------------------- + +Swift now supports: + + * Automatic crash catching and backtrace generation out of the box. + * Built-in symbolication. + * A choice of unwind algorithms, including "fast", DWARF and SEH. + * Interactive(!) crash/runtime error catching. + +Crash catching is enabled by default, and won't interfere with any system-wide +crash reporters you might be using. + +How do I configure backtracing? +------------------------------- + +There is an environment variable, ``SWIFT_BACKTRACE``, that can be used to +configure Swift's crash catching and backtracing support. The variable should +contain a ``,``-separated list of ``key=value`` pairs. Supported keys are as +follows: + ++-----------------+---------+--------------------------------------------------+ +| Key | Default | Meaning | ++=================+=========+==================================================+ +| enable | yes* | Set to ``no`` to disable crash catching, or | +| | | ``tty`` to enable only if stdin is a terminal. | ++-----------------+---------+--------------------------------------------------+ +| demangle | yes | Set to ``no`` to disable demangling. | ++-----------------+---------+--------------------------------------------------+ +| interactive | tty | Set to ``no`` to disable interaction, or ``yes`` | +| | | to enable always. | ++-----------------+---------+--------------------------------------------------+ +| color | tty | Set to ``yes`` to enable always, or ``no`` to | +| | | disable. Uses ANSI escape sequences. | ++-----------------+---------+--------------------------------------------------+ +| timeout | 30s | Time to wait for interaction when a crash | +| | | occurs. Setting this to ``none`` or ``0s`` will | +| | | disable interaction. | ++-----------------+---------+--------------------------------------------------+ +| unwind | auto | Specifies which unwind algorithm to use. | +| | | ``auto`` means to choose appropriately for the | +| | | platform. Other options are ``fast``, which | +| | | does a naïve stack walk; and ``precise``, which | +| | | uses exception handling data to perform an | +| | | unwind. | ++-----------------+---------+--------------------------------------------------+ +| preset | auto | Specifies which set of preset formatting options | +| | | to use. Options are ``friendly``, ``medium`` or | +| | | ``full``. ``auto`` means to use ``friendly`` if | +| | | interactive, and ``full`` otherwise. | ++-----------------+---------+--------------------------------------------------+ +| sanitize | preset | If ``yes``, we will try to process paths to | +| | | remove PII. Exact behaviour is platform | +| | | dependent. | ++-----------------+---------+--------------------------------------------------+ +| threads | preset | Options are ``all`` to show backtraces for every | +| | | thread, or ``crashed`` to show only the crashing | +| | | thread. | ++-----------------+---------+--------------------------------------------------+ +| registers | preset | Options are ``none``, ``all`` or ``crashed``. | ++-----------------+---------+--------------------------------------------------+ +| images | preset | Options are ``none``, ``all``, or ``mentioned``, | +| | | which only displays images mentioned in a | +| | | backtrace. | ++-----------------+---------+--------------------------------------------------+ +| limit | 64 | Limits the length of the captured backtrace. See | +| | | below for a discussion of its behaviour. Can be | +| | | set to ``none`` to mean no limit. | ++-----------------+---------+--------------------------------------------------+ +| top | 16 | Specify a minimum number of frames to capture | +| | | from the top of the stack. See below for more. | ++-----------------+---------+--------------------------------------------------+ +| swift-backtrace | | If specified, gives the full path to the | +| | | swift-backtrace binary to use for crashes. | +| | | Otherwise, Swift will locate the binary relative | +| | | to the runtime library, or using ``SWIFT_ROOT``. | ++-----------------+---------+--------------------------------------------------+ + +(*) On macOS, this defaults to ``tty`` rather than ``yes``. + +Backtrace limits +---------------- + +The limit settings are provided both to prevent runaway backtraces and to allow +for a sensible backtrace to be produced even when a function has blown the stack +through excessive recursion. + +Typically in the latter case you want to capture some frames at the top of the +stack so that you can see how the recursion was entered, and the frames at the +bottom of the stack where the actual fault occurred. + +1. There are ``limit`` or fewer frames. In this case we will display all + the frames in the backtrace. Note that this _includes_ the case where there + are exactly ``limit`` frames. + +2. There are more than ``limit`` frames. + + a. ``top`` is ``0``. We will display the first ``limit - 1`` frames followed + by ``...`` to indicate that more frames exist. + + b. ``top`` is less than ``limit - 1``. We will display ``limit - 1 - top`` + frames from the bottom of the stack, then a ``...``, then ``top`` frames + from the top of the stack. + + c. ``top`` is greater or equal to ``limit - 1``. We will display ``...``, + followed by ``limit - 1`` frames from the top of the stack. + +For example, let's say we have a stack containing 10 frames numbered here 1 to +10, with 10 being the innermost frame. With ``limit`` set to 5, you would see:: + + 10 + 9 + 8 + 7 + ... + +With ``limit`` set to 5 and ``top`` to 2, you would instead see:: + + 10 + 9 + ... + 2 + 1 + +And with ``limit`` set to 5 and ``top`` to 4 or above, you would see:: + + ... + 4 + 3 + 2 + 1 + +What is the swift-backtrace binary? +----------------------------------- + +``swift-backtrace`` is a program that gets invoked when your program crashes. +We do this because when a program crashes, it is potentially in an invalid state +and there is very little that is safe for us to do. By executing an external +helper program, we ensure that we do not interfere with the way the program was +going to crash (so that system-wide crash catchers will still generate the +correct information), and we are also able to use any functionality we need to +generate a decent backtrace, including symbolication (which might in general +require memory allocation, fetching and reading remote files and so on). + +You shouldn't try to run ``swift-backtrace`` yourself; it has unusual +requirements, which vary from platform to platform. Instead, it will be +triggered automatically by the runtime. diff --git a/docs/README.md b/docs/README.md index d0186877afdea..5cdda2f94b11f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -136,6 +136,9 @@ documentation, please create a thread on the Swift forums under the operations on [currency](/docs/Lexicon.md#currency-type) data types and optimizes accordingly. Includes a thorough discussion of the `@_semantics` attribute. +- Runtime specifics: + - [Backtracing.rst](/docs/Backtracing.rst): + Describes Swift's backtracing and crash catching support. ### SourceKit subsystems From 5809c020cdd16d6fd223423f94eec1d4c14dd10d Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Wed, 15 Feb 2023 16:45:28 +0000 Subject: [PATCH 21/22] [Backtracing][Docs] Add some information about signal handlers for macOS. Added a list of handled signals and some notes on what the runtime will do if it finds signal handlers already configured. rdar://105394365 --- docs/Backtracing.rst | 54 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/docs/Backtracing.rst b/docs/Backtracing.rst index 2c4eb9a3adbb6..7bf33badb74a0 100644 --- a/docs/Backtracing.rst +++ b/docs/Backtracing.rst @@ -167,3 +167,57 @@ require memory allocation, fetching and reading remote files and so on). You shouldn't try to run ``swift-backtrace`` yourself; it has unusual requirements, which vary from platform to platform. Instead, it will be triggered automatically by the runtime. + +System specifics +---------------- + +macOS +^^^^^ + +On macOS, we catch crashes and other events using a signal handler. At time of +writing, this is installed for the following signals: + ++--------------+--------------------------+-------------------------------------+ +| Signal | Description | Comment | ++====+=========+==========================+=====================================+ +| 3 | SIGQUIT | Quit program | | ++----+---------+--------------------------+-------------------------------------+ +| 4 | SIGILL | Illegal instruction | | ++----+---------+--------------------------+-------------------------------------+ +| 5 | SIGTRAP | Trace trap | | ++----+---------+--------------------------+-------------------------------------+ +| 6 | SIGABRT | Abort program | | ++----+---------+--------------------------+-------------------------------------+ +| 8 | SIGFPE | Floating point exception | On Intel, integer divide by zero | +| | | | also triggers this. | ++----+---------+--------------------------+-------------------------------------+ +| 10 | SIGBUS | Bus error | | ++----+---------+--------------------------+-------------------------------------+ +| 11 | SIGSEGV | Segmentation violation | | ++----+---------+--------------------------+-------------------------------------+ + +If crash catching is enabled, the signal handler will be installed for any +process that links the Swift runtime. If you replace the handlers for any of +these signals, your program will no longer produce backtraces for program +failures that lead to the handler you have replaced. + +Additionally, the runtime will configure an alternate signal handling stack, so +that stack overflows can be successfully trapped. + +Note that the runtime will not install its signal handlers for a signal if it +finds that there is already a handler for that signal. Similarly if something +else has already configured an alternate signal stack, it will leave that +stack alone. + +Once the backtracer has finished handling the crash, it will allow the crashing +program to continue and crash normally, which will result in the usual Crash +Reporter log file being generated. + +Crash catching *cannot* be enabled for setuid binaries. This is intentional as +doing so might create a security hole. + +Other Darwin (iOS, tvOS) +^^^^^^^^^^^^^^^^^^^^^^^^ + +Crash catching is not enabled for non-macOS Darwin. You should continue to look +at the system-provided crash logs. From 13af9c61142107d7cc97ae8bd2c52f8ff39ea3cd Mon Sep 17 00:00:00 2001 From: Alastair Houghton Date: Mon, 13 Feb 2023 19:54:02 +0000 Subject: [PATCH 22/22] [Backtracing] Add control over symbol caching. Some symbolication frameworks have a symbol cache; we probably don't want to use that for test cases, to avoid running into problems where the cache holds stale information. rdar://105409147 --- docs/Backtracing.rst | 4 + include/swift/Runtime/Backtrace.h | 1 + stdlib/public/Backtracing/Backtrace.swift | 9 +- .../Backtracing/CoreSymbolication.swift | 1 + .../Backtracing/SymbolicatedBacktrace.swift | 11 +- .../libexec/swift-backtrace/Target.swift | 14 +- .../public/libexec/swift-backtrace/main.swift | 18 +- stdlib/public/runtime/Backtrace.cpp | 5 + stdlib/public/runtime/CrashHandlerMacOS.cpp | 4 + test/Backtracing/Crash.swift | 10 +- test/Backtracing/CrashWithThunk.swift | 4 +- test/Backtracing/Overflow.swift | 14 +- test/Backtracing/StackOverflow.swift | 258 +++++++++--------- test/Backtracing/SymbolicatedBacktrace.swift | 2 +- .../SymbolicatedBacktraceInline.swift | 2 +- 15 files changed, 198 insertions(+), 159 deletions(-) diff --git a/docs/Backtracing.rst b/docs/Backtracing.rst index 7bf33badb74a0..b0c8cc7d13ee1 100644 --- a/docs/Backtracing.rst +++ b/docs/Backtracing.rst @@ -92,6 +92,10 @@ follows: | top | 16 | Specify a minimum number of frames to capture | | | | from the top of the stack. See below for more. | +-----------------+---------+--------------------------------------------------+ +| cache | yes | Set to ``no`` to disable symbol caching. This | +| | | only has effect on platforms that have a symbol | +| | | cache that can be controlled by the runtime. | ++-----------------+---------+--------------------------------------------------+ | swift-backtrace | | If specified, gives the full path to the | | | | swift-backtrace binary to use for crashes. | | | | Otherwise, Swift will locate the binary relative | diff --git a/include/swift/Runtime/Backtrace.h b/include/swift/Runtime/Backtrace.h index 7eac721f13463..0328c3ff150e2 100644 --- a/include/swift/Runtime/Backtrace.h +++ b/include/swift/Runtime/Backtrace.h @@ -111,6 +111,7 @@ struct BacktraceSettings { unsigned top; SanitizePaths sanitize; Preset preset; + bool cache; const char *swiftBacktracePath; }; diff --git a/stdlib/public/Backtracing/Backtrace.swift b/stdlib/public/Backtracing/Backtrace.swift index 4eb11d7e5daaa..1d244b1ee63a2 100644 --- a/stdlib/public/Backtracing/Backtrace.swift +++ b/stdlib/public/Backtracing/Backtrace.swift @@ -444,15 +444,20 @@ public struct Backtrace: CustomStringConvertible, Sendable { /// running on, add virtual frames to show inline /// function calls. /// + /// @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) + showInlineFrames: Bool = true, + useSymbolCache: Bool = true) -> SymbolicatedBacktrace? { return SymbolicatedBacktrace.symbolicate(backtrace: self, images: images, sharedCacheInfo: sharedCacheInfo, - showInlineFrames: showInlineFrames) + showInlineFrames: showInlineFrames, + useSymbolCache: useSymbolCache) } /// Provide a textual version of the backtrace. diff --git a/stdlib/public/Backtracing/CoreSymbolication.swift b/stdlib/public/Backtracing/CoreSymbolication.swift index bd5911f1374cb..ab090267301e1 100644 --- a/stdlib/public/Backtracing/CoreSymbolication.swift +++ b/stdlib/public/Backtracing/CoreSymbolication.swift @@ -186,6 +186,7 @@ func CSIsNull(_ obj: CSTypeRef) -> Bool { // .. CSSymbolicator ........................................................... +let kCSSymbolicatorDisallowDaemonCommunication = UInt32(0x00000800) struct BinaryRelocationInformation { var base: mach_vm_address_t diff --git a/stdlib/public/Backtracing/SymbolicatedBacktrace.swift b/stdlib/public/Backtracing/SymbolicatedBacktrace.swift index 0c035c9f8bc8f..c882bc5934721 100644 --- a/stdlib/public/Backtracing/SymbolicatedBacktrace.swift +++ b/stdlib/public/Backtracing/SymbolicatedBacktrace.swift @@ -259,6 +259,7 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { /// Create a symbolicator. private static func withSymbolicator(images: [Backtrace.Image], sharedCacheInfo: Backtrace.SharedCacheInfo?, + useSymbolCache: Bool, fn: (CSSymbolicatorRef) throws -> T) rethrows -> T { let binaryImageList = images.map{ image in BinaryImageInformation( @@ -279,7 +280,9 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { } let symbolicator = CSSymbolicatorCreateWithBinaryImageList( - binaryImageList, 0, nil + binaryImageList, + useSymbolCache ? 0 : kCSSymbolicatorDisallowDaemonCommunication, + nil ) defer { CSRelease(symbolicator) } @@ -345,7 +348,8 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { internal static func symbolicate(backtrace: Backtrace, images: [Backtrace.Image]?, sharedCacheInfo: Backtrace.SharedCacheInfo?, - showInlineFrames: Bool) + showInlineFrames: Bool, + useSymbolCache: Bool) -> SymbolicatedBacktrace? { let theImages: [Backtrace.Image] @@ -370,7 +374,8 @@ public struct SymbolicatedBacktrace: CustomStringConvertible { #if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) withSymbolicator(images: theImages, - sharedCacheInfo: theCacheInfo) { symbolicator in + sharedCacheInfo: theCacheInfo, + useSymbolCache: useSymbolCache) { symbolicator in for frame in backtrace.frames { switch frame { case .omittedFrames(_), .truncated: diff --git a/stdlib/public/libexec/swift-backtrace/Target.swift b/stdlib/public/libexec/swift-backtrace/Target.swift index a0105626a44c9..7bf22ff723bce 100644 --- a/stdlib/public/libexec/swift-backtrace/Target.swift +++ b/stdlib/public/libexec/swift-backtrace/Target.swift @@ -141,7 +141,7 @@ class Target { } } - init(crashInfoAddr: UInt64, limit: Int?, top: Int) { + init(crashInfoAddr: UInt64, limit: Int?, top: Int, cache: Bool) { pid = getppid() if let parentTask = Self.getParentTask() { task = parentTask @@ -177,10 +177,10 @@ class Target { images = Backtrace.captureImages(for: task) sharedCacheInfo = Backtrace.captureSharedCacheInfo(for: task) - fetchThreads(limit: limit, top: top) + fetchThreads(limit: limit, top: top, cache: cache) } - func fetchThreads(limit: Int?, top: Int) { + func fetchThreads(limit: Int?, top: Int, cache: Bool) { var threadPorts: thread_act_array_t? = nil var threadCount: mach_msg_type_number_t = 0 let kr = task_threads(task, @@ -248,7 +248,8 @@ class Target { } guard let symbolicated = backtrace.symbolicated(with: images, - sharedCacheInfo: sharedCacheInfo) else { + sharedCacheInfo: sharedCacheInfo, + useSymbolCache: cache) else { print("unable to symbolicate backtrace from context for thread \(ndx)") exit(1) } @@ -262,7 +263,7 @@ class Target { } } - public func redoBacktraces(limit: Int?, top: Int) { + public func redoBacktraces(limit: Int?, top: Int, cache: Bool) { for (ndx, thread) in threads.enumerated() { guard let context = thread.context else { continue @@ -277,7 +278,8 @@ class Target { } guard let symbolicated = backtrace.symbolicated(with: images, - sharedCacheInfo: sharedCacheInfo) else { + sharedCacheInfo: sharedCacheInfo, + useSymbolCache: cache) else { print("unable to symbolicate backtrace from context for thread \(ndx)") continue } diff --git a/stdlib/public/libexec/swift-backtrace/main.swift b/stdlib/public/libexec/swift-backtrace/main.swift index 2e9fa95e57ccd..26394ddf9b356 100644 --- a/stdlib/public/libexec/swift-backtrace/main.swift +++ b/stdlib/public/libexec/swift-backtrace/main.swift @@ -65,6 +65,7 @@ internal struct SwiftBacktrace { var limit: Int? = 64 var top = 16 var sanitize: Bool? = nil + var cache = true } static var args = Arguments() @@ -83,7 +84,7 @@ internal struct SwiftBacktrace { static func usage() { print(""" -usage: swift-backtrace [--unwind ] [--demangle []] [--interactive []] [--color []] [--timeout ] [--preset ] [--threads []] [--registers ] [--images ] --crashinfo +usage: swift-backtrace [--unwind ] [--demangle []] [--interactive []] [--color []] [--timeout ] [--preset ] [--threads []] [--registers ] [--images ] [--cache []] --crashinfo Generate a backtrace for the parent process. @@ -130,6 +131,8 @@ Generate a backtrace for the parent process. --sanitize [] -s [] Set whether or not to sanitize paths. +--cache [] Set whether or not to use the symbol cache, if any. + --crashinfo -a Provide a pointer to a platform specific CrashInfo structure. should be in hexadecimal. @@ -295,6 +298,12 @@ Generate a backtrace for the parent process. } else { args.sanitize = true } + case "--cache": + if let v = value { + args.cache = parseBool(v) + } else { + args.cache = true + } case "-a", "--crashinfo": if let v = value { if let a = UInt64(v, radix: 16) { @@ -381,7 +390,8 @@ Generate a backtrace for the parent process. formattingOptions = formattingOptions.showImages(.none) target = Target(crashInfoAddr: crashInfoAddr, - limit: args.limit, top: args.top) + limit: args.limit, top: args.top, + cache: args.cache) currentThread = target!.crashingThreadNdx @@ -903,7 +913,9 @@ Generate a backtrace for the parent process. } if changedBacktrace { - target.redoBacktraces(limit: args.limit, top: args.top) + target.redoBacktraces(limit: args.limit, + top: args.top, + cache: args.cache) } } } diff --git a/stdlib/public/runtime/Backtrace.cpp b/stdlib/public/runtime/Backtrace.cpp index 2f5010a68cf84..971f152d8e493 100644 --- a/stdlib/public/runtime/Backtrace.cpp +++ b/stdlib/public/runtime/Backtrace.cpp @@ -104,6 +104,9 @@ SWIFT_RUNTIME_STDLIB_INTERNAL BacktraceSettings _swift_backtraceSettings = { // preset Preset::Auto, + // cache + true, + // swiftBacktracePath NULL, }; @@ -599,6 +602,8 @@ _swift_processBacktracingSetting(llvm::StringRef key, "swift runtime: bad backtrace top count '%.*s'\n", static_cast(value.size()), value.data()); } + } else if (key.equals_insensitive("cache")) { + _swift_backtraceSettings.cache = parseBoolean(value); } else if (key.equals_insensitive("swift-backtrace")) { size_t len = value.size(); char *path = (char *)std::malloc(len + 1); diff --git a/stdlib/public/runtime/CrashHandlerMacOS.cpp b/stdlib/public/runtime/CrashHandlerMacOS.cpp index 17a2e0919143f..12719b6786fc5 100644 --- a/stdlib/public/runtime/CrashHandlerMacOS.cpp +++ b/stdlib/public/runtime/CrashHandlerMacOS.cpp @@ -268,6 +268,8 @@ const char *backtracer_argv[] = { top_buf, // 24 "--sanitize", // 25 "preset", // 26 + "--cache", // 27 + "true", // 28 NULL }; @@ -440,6 +442,8 @@ run_backtracer() break; } + backtracer_argv[28] = trueOrFalse(_swift_backtraceSettings.cache); + format_unsigned(_swift_backtraceSettings.timeout, timeout_buf); if (_swift_backtraceSettings.limit < 0) diff --git a/test/Backtracing/Crash.swift b/test/Backtracing/Crash.swift index 2528049d923ad..e6a7ba236a681 100644 --- a/test/Backtracing/Crash.swift +++ b/test/Backtracing/Crash.swift @@ -7,11 +7,11 @@ // RUN: %target-codesign %t/CrashNoDebug // RUN: %target-codesign %t/CrashOpt // RUN: %target-codesign %t/CrashOptNoDebug -// RUN: (env SWIFT_BACKTRACE=enable=yes %target-run %t/Crash || true) | %FileCheck %s -// RUN: (env SWIFT_BACKTRACE=preset=friendly,enable=yes %target-run %t/Crash || true) | %FileCheck %s --check-prefix FRIENDLY -// RUN: (env SWIFT_BACKTRACE=enable=yes %target-run %t/CrashNoDebug || true) | %FileCheck %s --check-prefix NODEBUG -// RUN: (env SWIFT_BACKTRACE=enable=yes %target-run %t/CrashOpt || true) | %FileCheck %s --check-prefix OPTIMIZED -// RUN: (env SWIFT_BACKTRACE=enable=yes %target-run %t/CrashOptNoDebug || true) | %FileCheck %s --check-prefix OPTNODEBUG +// RUN: (env SWIFT_BACKTRACE=enable=yes,cache=no %target-run %t/Crash || true) | %FileCheck %s +// RUN: (env SWIFT_BACKTRACE=preset=friendly,enable=yes,cache=no %target-run %t/Crash || true) | %FileCheck %s --check-prefix FRIENDLY +// RUN: (env SWIFT_BACKTRACE=enable=yes,cache=no %target-run %t/CrashNoDebug || true) | %FileCheck %s --check-prefix NODEBUG +// RUN: (env SWIFT_BACKTRACE=enable=yes,cache=no %target-run %t/CrashOpt || true) | %FileCheck %s --check-prefix OPTIMIZED +// RUN: (env SWIFT_BACKTRACE=enable=yes,cache=no %target-run %t/CrashOptNoDebug || true) | %FileCheck %s --check-prefix OPTNODEBUG // REQUIRES: executable_test // REQUIRES: OS=macosx diff --git a/test/Backtracing/CrashWithThunk.swift b/test/Backtracing/CrashWithThunk.swift index f77ddbc89f354..5ae5928dd54dc 100644 --- a/test/Backtracing/CrashWithThunk.swift +++ b/test/Backtracing/CrashWithThunk.swift @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift %s -parse-as-library -Onone -g -o %t/CrashWithThunk // RUN: %target-codesign %t/CrashWithThunk -// RUN: (env SWIFT_BACKTRACE=enable=yes %target-run %t/CrashWithThunk || true) | %FileCheck %s -// RUN: (env SWIFT_BACKTRACE=preset=friendly,enable=yes %target-run %t/CrashWithThunk || true) | %FileCheck %s --check-prefix FRIENDLY +// RUN: (env SWIFT_BACKTRACE=enable=yes,cache=no %target-run %t/CrashWithThunk || true) | %FileCheck %s +// RUN: (env SWIFT_BACKTRACE=preset=friendly,enable=yes,cache=no %target-run %t/CrashWithThunk || true) | %FileCheck %s --check-prefix FRIENDLY // REQUIRES: executable_test // REQUIRES: OS=macosx diff --git a/test/Backtracing/Overflow.swift b/test/Backtracing/Overflow.swift index 60d41c85ed086..a435f00175866 100644 --- a/test/Backtracing/Overflow.swift +++ b/test/Backtracing/Overflow.swift @@ -1,8 +1,8 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift %s -parse-as-library -Onone -g -o %t/Overflow // RUN: %target-codesign %t/Overflow -// RUN: (env SWIFT_BACKTRACE=enable=yes %target-run %t/Overflow || true) | %FileCheck %s -// RUN: (env SWIFT_BACKTRACE=preset=friendly,enable=yes %target-run %t/Overflow || true) | %FileCheck %s --check-prefix FRIENDLY +// RUN: (env SWIFT_BACKTRACE=enable=yes,cache=no %target-run %t/Overflow || true) | %FileCheck %s +// RUN: (env SWIFT_BACKTRACE=preset=friendly,enable=yes,cache=no %target-run %t/Overflow || true) | %FileCheck %s --check-prefix FRIENDLY // REQUIRES: executable_test // REQUIRES: OS=macosx @@ -42,11 +42,11 @@ struct Overflow { // CHECK: Thread 0 crashed: // CHECK: 0 [inlined] [system] 0x{{[0-9a-f]+}} Swift runtime failure: arithmetic overflow in Overflow at {{.*}}/ -// CHECK-NEXT: 1 0x{{[0-9a-f]+}} level5() + {{[0-9]+}} in Overflow at {{.*}}/Overflow.swift:{{30|29}}:5 -// CHECK-NEXT: 2 [ra] 0x{{[0-9a-f]+}} level4() + {{[0-9]+}} in Overflow at {{.*}}/Overflow.swift:{{24|23}}:3 -// CHECK-NEXT: 3 [ra] 0x{{[0-9a-f]+}} level3() + {{[0-9]+}} in Overflow at {{.*}}/Overflow.swift:{{20|19}}:3 -// CHECK-NEXT: 4 [ra] 0x{{[0-9a-f]+}} level2() + {{[0-9]+}} in Overflow at {{.*}}/Overflow.swift:{{16|15}}:3 -// CHECK-NEXT: 5 [ra] 0x{{[0-9a-f]+}} level1() + {{[0-9]+}} in Overflow at {{.*}}/Overflow.swift:{{12|11}}:3 +// CHECK-NEXT: 1 0x{{[0-9a-f]+}} level5() + {{[0-9]+}} in Overflow at {{.*}}/Overflow.swift:30:5 +// CHECK-NEXT: 2 [ra] 0x{{[0-9a-f]+}} level4() + {{[0-9]+}} in Overflow at {{.*}}/Overflow.swift:24:3 +// CHECK-NEXT: 3 [ra] 0x{{[0-9a-f]+}} level3() + {{[0-9]+}} in Overflow at {{.*}}/Overflow.swift:20:3 +// CHECK-NEXT: 4 [ra] 0x{{[0-9a-f]+}} level2() + {{[0-9]+}} in Overflow at {{.*}}/Overflow.swift:16:3 +// CHECK-NEXT: 5 [ra] 0x{{[0-9a-f]+}} level1() + {{[0-9]+}} in Overflow at {{.*}}/Overflow.swift:12:3 // CHECK-NEXT: 6 [ra] 0x{{[0-9a-f]+}} static Overflow.main() + {{[0-9]+}} in Overflow at {{.*}}/Overflow.swift:36:5 // CHECK-NEXT: 7 [ra] [system] 0x{{[0-9a-f]+}} static Overflow.$main() + {{[0-9]+}} in Overflow at {{.*}}/Overflow.swift:33:1 // CHECK-NEXT: 8 [ra] [system] 0x{{[0-9a-f]+}} main + {{[0-9]+}} in Overflow at {{.*}}/Overflow.swift diff --git a/test/Backtracing/StackOverflow.swift b/test/Backtracing/StackOverflow.swift index e35b8d7b4bc2f..775dc90bf434a 100644 --- a/test/Backtracing/StackOverflow.swift +++ b/test/Backtracing/StackOverflow.swift @@ -1,9 +1,9 @@ // RUN: %empty-directory(%t) // RUN: %target-build-swift %s -parse-as-library -Onone -g -o %t/StackOverflow // RUN: %target-codesign %t/StackOverflow -// RUN: (env SWIFT_BACKTRACE=enable=yes %target-run %t/StackOverflow || true) | %FileCheck %s -// RUN: (env SWIFT_BACKTRACE=limit=16,top=4,enable=yes %target-run %t/StackOverflow || true) | %FileCheck %s --check-prefix LIMITED -// RUN: (env SWIFT_BACKTRACE=preset=friendly,enable=yes %target-run %t/StackOverflow || true) | %FileCheck %s --check-prefix FRIENDLY +// RUN: (env SWIFT_BACKTRACE=enable=yes,cache=no %target-run %t/StackOverflow || true) | %FileCheck %s +// RUN: (env SWIFT_BACKTRACE=limit=16,top=4,enable=yes,cache=no %target-run %t/StackOverflow || true) | %FileCheck %s --check-prefix LIMITED +// RUN: (env SWIFT_BACKTRACE=preset=friendly,enable=yes,cache=no %target-run %t/StackOverflow || true) | %FileCheck %s --check-prefix FRIENDLY // REQUIRES: executable_test // REQUIRES: OS=macosx @@ -31,65 +31,65 @@ struct StackOverflow { // CHECK: Thread 0 crashed: // CHECK: 0 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{[0-9]+}} -// CHECK-NEXT: 1 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 2 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 3 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 4 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 5 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 6 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 7 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 8 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 9 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 10 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 11 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 12 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 13 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 14 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 15 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 16 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 17 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 18 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 19 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 20 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 21 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 22 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 23 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 24 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 25 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 26 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 27 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 28 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 29 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 30 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 31 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 32 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 33 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 34 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 35 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 36 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 37 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 38 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 39 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 40 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 41 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 42 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 43 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 44 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 45 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: 46 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: 1 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 2 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 3 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 4 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 5 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 6 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 7 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 8 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 9 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 10 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 11 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 12 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 13 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 14 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 15 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 16 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 17 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 18 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 19 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 20 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 21 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 22 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 23 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 24 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 25 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 26 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 27 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 28 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 29 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 30 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 31 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 32 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 33 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 34 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 35 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 36 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 37 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 38 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 39 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 40 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 41 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 42 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 43 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 44 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 45 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: 46 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 // CHECK-NEXT: ... -// CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 // CHECK-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} static StackOverflow.main() + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{21|20}}:5 // CHECK-NEXT: {{[0-9]+}} [ra] [system] 0x{{[0-9a-f]+}} static StackOverflow.$main() + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{18|17}}:1 // CHECK-NEXT: {{[0-9]+}} [ra] [system] 0x{{[0-9a-f]+}} main + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift @@ -106,16 +106,16 @@ struct StackOverflow { // LIMITED: Thread 0 crashed: // LIMITED: 0 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{[0-9]+}} -// LIMITED-NEXT: 1 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// LIMITED-NEXT: 2 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// LIMITED-NEXT: 3 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// LIMITED-NEXT: 4 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// LIMITED-NEXT: 5 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// LIMITED-NEXT: 6 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// LIMITED-NEXT: 7 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// LIMITED-NEXT: 8 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// LIMITED-NEXT: 9 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// LIMITED-NEXT: 10 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// LIMITED-NEXT: 1 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// LIMITED-NEXT: 2 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// LIMITED-NEXT: 3 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// LIMITED-NEXT: 4 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// LIMITED-NEXT: 5 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// LIMITED-NEXT: 6 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// LIMITED-NEXT: 7 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// LIMITED-NEXT: 8 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// LIMITED-NEXT: 9 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// LIMITED-NEXT: 10 [ra] 0x{{[0-9a-f]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 // LIMITED-NEXT: ... // LIMITED-NEXT: {{[0-9]+}} [ra] 0x{{[0-9a-f]+}} static StackOverflow.main() + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{21|20}}:5 // LIMITED-NEXT: {{[0-9]+}} [ra] [system] 0x{{[0-9a-f]+}} static StackOverflow.$main() + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{18|17}}:1 @@ -135,7 +135,7 @@ struct StackOverflow { // SKIP-FRIENDLY-NEXT: │ ▲ // SKIP-FRIENDLY-NEXT: 12│ if level % 100000 == 0 { -// FRIENDLY: 1 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY: 1 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 // SKIP-FRIENDLY: 12│ if level % 100000 == 0 { // SKIP-FRIENDLY-NEXT: 13│ print(level) @@ -144,64 +144,64 @@ struct StackOverflow { // SKIP-FRIENDLY-NEXT: │ ▲ // SKIP-FRIENDLY-NEXT: 16│ } -// FRIENDLY: 2 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: 3 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: 4 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: 5 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: 6 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: 7 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: 8 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: 9 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: 10 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: 11 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: 12 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: 13 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: 14 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: 15 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: 16 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: 17 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: 18 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: 19 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: 20 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: 21 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: 22 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: 23 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: 24 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: 25 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: 26 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: 27 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: 28 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: 29 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: 30 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: 31 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: 32 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: 33 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: 34 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: 35 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: 36 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: 37 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: 38 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: 39 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: 40 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: 41 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: 42 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: 43 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: 44 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: 45 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: 46 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY: 2 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: 3 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: 4 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: 5 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: 6 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: 7 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: 8 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: 9 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: 10 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: 11 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: 12 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: 13 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: 14 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: 15 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: 16 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: 17 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: 18 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: 19 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: 20 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: 21 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: 22 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: 23 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: 24 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: 25 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: 26 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: 27 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: 28 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: 29 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: 30 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: 31 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: 32 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: 33 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: 34 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: 35 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: 36 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: 37 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: 38 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: 39 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: 40 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: 41 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: 42 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: 43 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: 44 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: 45 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: 46 recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 // FRIENDLY-NEXT: ... -// FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 -// FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{15|14}}:3 +// FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 +// FRIENDLY-NEXT: {{[0-9]+}} recurse(_:) + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:15:3 // FRIENDLY-NEXT: {{[0-9]+}} static StackOverflow.main() + {{[0-9]+}} in StackOverflow at {{.*}}/StackOverflow.swift:{{21|20}}:5 // SKIP-FRIENDLY: 18│ @main diff --git a/test/Backtracing/SymbolicatedBacktrace.swift b/test/Backtracing/SymbolicatedBacktrace.swift index 23a692f93032b..1ac9978869310 100644 --- a/test/Backtracing/SymbolicatedBacktrace.swift +++ b/test/Backtracing/SymbolicatedBacktrace.swift @@ -29,7 +29,7 @@ func splat() { } func pow() { - let backtrace = try! Backtrace.capture().symbolicated()! + let backtrace = try! Backtrace.capture().symbolicated(useSymbolCache: false)! // CHECK: 0{{[ \t]+}}0x{{[0-9a-f]+}} [ra] [0] SymbolicatedBacktrace pow() // CHECK: 1{{[ \t]+}}0x{{[0-9a-f]+}} [ra] [0] SymbolicatedBacktrace splat() diff --git a/test/Backtracing/SymbolicatedBacktraceInline.swift b/test/Backtracing/SymbolicatedBacktraceInline.swift index c8701669fb8b2..a368e401a50c2 100644 --- a/test/Backtracing/SymbolicatedBacktraceInline.swift +++ b/test/Backtracing/SymbolicatedBacktraceInline.swift @@ -29,7 +29,7 @@ func splat() { } func pow() { - let backtrace = try! Backtrace.capture().symbolicated()! + let backtrace = try! Backtrace.capture().symbolicated(useSymbolCache: false)! // CHECK: 0{{[ \t]+}}0x{{[0-9a-f]+}} [ra] [0] SymbolicatedBacktraceInline pow() // CHECK: 1{{[ \t]+}}0x{{[0-9a-f]+}} [ra] [inlined] [0] SymbolicatedBacktraceInline splat()