mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
When we crash, emit a message straight away that says we're working on a backtrace. If starting the backtracer fails, report that also. Finally, add a duration to the messages output by the backtracer, so that we can see how long it took. rdar://118055527
1046 lines
31 KiB
C++
1046 lines
31 KiB
C++
//===--- 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 <type_traits>
|
|
|
|
#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 __linux__
|
|
#include <sys/auxv.h>
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
#else
|
|
#if __has_include(<sys/mman.h>)
|
|
#include <sys/mman.h>
|
|
#define PROTECT_BACKTRACE_SETTINGS 1
|
|
#else
|
|
#define PROTECT_BACKTRACE_SETTINGS 0
|
|
#warning Backtracer settings will not be protected in this configuration.
|
|
#endif
|
|
|
|
#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
|
|
#include <spawn.h>
|
|
#endif
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <cerrno>
|
|
|
|
#ifdef _WIN32
|
|
// We'll probably want dbghelp.h here
|
|
#else
|
|
#include <cxxabi.h>
|
|
#endif
|
|
|
|
#include "BacktracePrivate.h"
|
|
|
|
#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 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,
|
|
|
|
// cache
|
|
true,
|
|
|
|
// outputTo,
|
|
OutputTo::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 || TARGET_OS_MACCATALYST
|
|
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)
|
|
|
|
#if defined(SWIFT_RUNTIME_FIXED_BACKTRACER_PATH)
|
|
const WCHAR swiftBacktracePath[] = L"" SWIFT_RUNTIME_FIXED_BACKTRACER_PATH;
|
|
#else
|
|
__declspec(allocate(SWIFT_BACKTRACE_SECTION)) WCHAR swiftBacktracePath[SWIFT_BACKTRACE_BUFFER_SIZE];
|
|
#endif // !defined(SWIFT_RUNTIME_FIXED_BACKTRACER_PATH)
|
|
|
|
__declspec(allocate(SWIFT_BACKTRACE_SECTION)) CHAR swiftBacktraceEnv[SWIFT_BACKTRACE_ENVIRONMENT_SIZE];
|
|
|
|
#elif defined(__linux__) || TARGET_OS_OSX
|
|
|
|
#if defined(SWIFT_RUNTIME_FIXED_BACKTRACER_PATH)
|
|
const char swiftBacktracePath[] = SWIFT_RUNTIME_FIXED_BACKTRACER_PATH;
|
|
#else
|
|
char swiftBacktracePath[SWIFT_BACKTRACE_BUFFER_SIZE] __attribute__((section(SWIFT_BACKTRACE_SECTION), aligned(SWIFT_PAGE_SIZE)));
|
|
#endif // !defined(SWIFT_RUNTIME_FIXED_BACKTRACER_PATH
|
|
|
|
char swiftBacktraceEnv[SWIFT_BACKTRACE_ENVIRONMENT_SIZE] __attribute__((section(SWIFT_BACKTRACE_SECTION), aligned(SWIFT_PAGE_SIZE)));
|
|
#endif // defined(__linux__) || TARGET_OS_OSX
|
|
|
|
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
|
|
|
|
#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();
|
|
|
|
// Force off for setuid processes.
|
|
if (isPrivileged()) {
|
|
_swift_backtraceSettings.enabled = OnOffTty::Off;
|
|
}
|
|
|
|
if (backtracing)
|
|
_swift_parseBacktracingSettings(backtracing);
|
|
|
|
#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 (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;
|
|
}
|
|
|
|
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.outputTo == OutputTo::Auto) {
|
|
if (_swift_backtraceSettings.interactive == OnOffTty::On)
|
|
_swift_backtraceSettings.outputTo = OutputTo::Stdout;
|
|
else
|
|
_swift_backtraceSettings.outputTo = OutputTo::Stderr;
|
|
}
|
|
|
|
#if !defined(SWIFT_RUNTIME_FIXED_BACKTRACER_PATH)
|
|
if (_swift_backtraceSettings.enabled == OnOffTty::On
|
|
&& !_swift_backtraceSettings.swiftBacktracePath) {
|
|
_swift_backtraceSettings.swiftBacktracePath
|
|
= swift_copyAuxiliaryExecutablePath("swift-backtrace");
|
|
|
|
if (!_swift_backtraceSettings.swiftBacktracePath) {
|
|
// Disabled warning for now - rdar://106813646
|
|
/* swift::warning(0,
|
|
"swift runtime: unable to locate swift-backtrace; "
|
|
"disabling backtracing.\n"); */
|
|
_swift_backtraceSettings.enabled = OnOffTty::Off;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
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;
|
|
|
|
#if !defined(SWIFT_RUNTIME_FIXED_BACKTRACER_PATH)
|
|
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;
|
|
}
|
|
#endif // !defined(SWIFT_RUNTIME_FIXED_BACKTRACER_PATH)
|
|
|
|
_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;
|
|
|
|
#if !defined(SWIFT_RUNTIME_FIXED_BACKTRACER_PATH)
|
|
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 PROTECT_BACKTRACE_SETTINGS
|
|
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;
|
|
}
|
|
#endif // PROTECT_BACKTRACE_SETTINGS
|
|
}
|
|
#endif // !defined(SWIFT_RUNTIME_FIXED_BACKTRACER_PATH)
|
|
|
|
_swift_backtraceSetupEnvironment();
|
|
|
|
#if PROTECT_BACKTRACE_SETTINGS
|
|
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 // PROTECT_BACKTRACE_SETTINGS
|
|
|
|
#endif // !_WIN32
|
|
}
|
|
|
|
if (_swift_backtraceSettings.enabled == OnOffTty::On) {
|
|
#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
|
|
// Make sure that all fds are closed except for stdin/stdout/stderr.
|
|
posix_spawnattr_init(&backtraceSpawnAttrs);
|
|
posix_spawnattr_setflags(&backtraceSpawnAttrs, 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
|
|
|
|
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<int>(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<int>(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<int>(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<int>(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<int>(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<int>(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<int>(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<int>(value.size()), value.data());
|
|
}
|
|
} else if (key.equals_insensitive("cache")) {
|
|
_swift_backtraceSettings.cache = parseBoolean(value);
|
|
} else if (key.equals_insensitive("output-to")) {
|
|
if (value.equals_insensitive("auto"))
|
|
_swift_backtraceSettings.outputTo = OutputTo::Auto;
|
|
else if (value.equals_insensitive("stdout"))
|
|
_swift_backtraceSettings.outputTo = OutputTo::Stdout;
|
|
else if (value.equals_insensitive("stderr"))
|
|
_swift_backtraceSettings.outputTo = OutputTo::Stderr;
|
|
else {
|
|
swift::warning(0,
|
|
"swift runtime: unknown output-to setting '%.*s'\n",
|
|
static_cast<int>(value.size()), value.data());
|
|
}
|
|
#if !defined(SWIFT_RUNTIME_FIXED_BACKTRACER_PATH)
|
|
} 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<char *>(_swift_backtraceSettings.swiftBacktracePath));
|
|
_swift_backtraceSettings.swiftBacktracePath = path;
|
|
#endif // !defined(SWIFT_RUNTIME_FIXED_BACKTRACER_PATH)
|
|
} else {
|
|
swift::warning(0,
|
|
"swift runtime: unknown backtracing setting '%.*s'\n",
|
|
static_cast<int>(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;
|
|
}
|
|
|
|
#ifdef __linux__
|
|
struct spawn_info {
|
|
const char *path;
|
|
char * const *argv;
|
|
char * const *envp;
|
|
int memserver;
|
|
};
|
|
|
|
uint8_t spawn_stack[4096] __attribute__((aligned(SWIFT_PAGE_SIZE)));
|
|
|
|
int
|
|
do_spawn(void *ptr) {
|
|
struct spawn_info *pinfo = (struct spawn_info *)ptr;
|
|
|
|
/* Ensure that the memory server is always on fd 4 */
|
|
if (pinfo->memserver != 4) {
|
|
dup2(pinfo->memserver, 4);
|
|
close(pinfo->memserver);
|
|
}
|
|
|
|
/* Clear the signal mask */
|
|
sigset_t mask;
|
|
sigfillset(&mask);
|
|
sigprocmask(SIG_UNBLOCK, &mask, NULL);
|
|
|
|
return execvpe(pinfo->path, pinfo->argv, pinfo->envp);
|
|
}
|
|
|
|
int
|
|
safe_spawn(pid_t *ppid, const char *path, int memserver,
|
|
char * const argv[], char * const envp[])
|
|
{
|
|
struct spawn_info info = { path, argv, envp, memserver };
|
|
|
|
/* The CLONE_VFORK is *required* because info is on the stack; we don't
|
|
want to return until *after* the subprocess has called execvpe(). */
|
|
int ret = clone(do_spawn, spawn_stack + sizeof(spawn_stack),
|
|
CLONE_VFORK|CLONE_VM, &info);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
close(memserver);
|
|
|
|
*ppid = ret;
|
|
return 0;
|
|
}
|
|
#endif // defined(__linux__)
|
|
|
|
#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 bool
|
|
_swift_backtrace_isThunkFunction(const char *mangledName) {
|
|
swift::Demangle::Context ctx;
|
|
|
|
return ctx.isThunkSymbol(mangledName);
|
|
}
|
|
|
|
// Try to demangle a symbol.
|
|
SWIFT_RUNTIME_STDLIB_SPI char *
|
|
_swift_backtrace_demangle(const char *mangledName,
|
|
size_t mangledNameLength,
|
|
char *outputBuffer,
|
|
size_t *outputBufferSize) {
|
|
llvm::StringRef name = llvm::StringRef(mangledName, mangledNameLength);
|
|
|
|
// You must provide buffer size if you're providing your own output buffer
|
|
if (outputBuffer && !outputBufferSize) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (Demangle::isSwiftSymbol(name)) {
|
|
// This is a Swift mangling
|
|
auto options = DemangleOptions::SimplifiedUIDemangleOptions();
|
|
auto result = Demangle::demangleSymbolAsString(name, options);
|
|
size_t bufferSize;
|
|
|
|
if (outputBufferSize) {
|
|
bufferSize = *outputBufferSize;
|
|
*outputBufferSize = result.length() + 1;
|
|
}
|
|
|
|
if (outputBuffer == nullptr) {
|
|
outputBuffer = (char *)::malloc(result.length() + 1);
|
|
bufferSize = result.length() + 1;
|
|
}
|
|
|
|
size_t toCopy = std::min(bufferSize - 1, result.length());
|
|
::memcpy(outputBuffer, result.data(), toCopy);
|
|
outputBuffer[toCopy] = '\0';
|
|
|
|
return outputBuffer;
|
|
#ifndef _WIN32
|
|
} else if (name.startswith("_Z")) {
|
|
// Try C++; note that we don't want to force callers to use malloc() to
|
|
// allocate their buffer, which is a requirement for __cxa_demangle
|
|
// because it may call realloc() on the incoming pointer. As a result,
|
|
// we never pass the caller's buffer to __cxa_demangle.
|
|
size_t resultLen;
|
|
int status = 0;
|
|
char *result = abi::__cxa_demangle(mangledName, nullptr, &resultLen, &status);
|
|
|
|
if (result) {
|
|
size_t bufferSize;
|
|
|
|
if (outputBufferSize) {
|
|
bufferSize = *outputBufferSize;
|
|
*outputBufferSize = resultLen;
|
|
}
|
|
|
|
if (outputBuffer == nullptr) {
|
|
return result;
|
|
}
|
|
|
|
size_t toCopy = std::min(bufferSize - 1, resultLen - 1);
|
|
::memcpy(outputBuffer, result, toCopy);
|
|
outputBuffer[toCopy] = '\0';
|
|
|
|
free(result);
|
|
|
|
return outputBuffer;
|
|
}
|
|
#else
|
|
// On Windows, the mangling is different.
|
|
// ###TODO: Call __unDName()
|
|
#endif
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
// 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
|
|
#ifdef __linux__
|
|
_swift_spawnBacktracer(const ArgChar * const *argv, int memserver_fd)
|
|
#else
|
|
_swift_spawnBacktracer(const ArgChar * const *argv)
|
|
#endif
|
|
{
|
|
#if !SWIFT_BACKTRACE_ON_CRASH_SUPPORTED
|
|
return false;
|
|
#elif TARGET_OS_OSX || TARGET_OS_MACCATALYST || defined(__linux__)
|
|
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.
|
|
#ifdef __linux__
|
|
int ret = safe_spawn(&child, swiftBacktracePath, memserver_fd,
|
|
const_cast<char * const *>(argv),
|
|
const_cast<char * const *>(env));
|
|
#else
|
|
int ret = posix_spawn(&child, swiftBacktracePath,
|
|
&backtraceFileActions, &backtraceSpawnAttrs,
|
|
const_cast<char * const *>(argv),
|
|
const_cast<char * const *>(env));
|
|
#endif
|
|
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: Windows
|
|
#endif
|
|
}
|
|
|
|
// 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 void
|
|
_swift_displayCrashMessage(int signum, const void *pc)
|
|
{
|
|
#if !SWIFT_BACKTRACE_ON_CRASH_SUPPORTED
|
|
return;
|
|
#else
|
|
int fd = STDOUT_FILENO;
|
|
|
|
if (_swift_backtraceSettings.outputTo == OutputTo::Stderr)
|
|
fd = STDERR_FILENO;
|
|
|
|
const char *intro;
|
|
if (_swift_backtraceSettings.color == OnOffTty::On) {
|
|
intro = "\n💣 \033[91mProgram crashed: ";
|
|
} else {
|
|
intro = "\n*** Program crashed: ";
|
|
}
|
|
write(fd, intro, strlen(intro));
|
|
|
|
char sigbuf[30];
|
|
strcpy(sigbuf, "Signal ");
|
|
_swift_formatUnsigned((unsigned)signum, sigbuf + 7);
|
|
write(fd, sigbuf, strlen(sigbuf));
|
|
|
|
const char *message;
|
|
if (!pc) {
|
|
message = ": Backtracing";
|
|
} else {
|
|
message = ": Backtracing from 0x";
|
|
}
|
|
write(fd, message, strlen(message));
|
|
|
|
if (pc) {
|
|
char pcbuf[18];
|
|
_swift_formatAddress(pc, pcbuf);
|
|
write(fd, pcbuf, strlen(pcbuf));
|
|
}
|
|
|
|
const char *outro;
|
|
if (_swift_backtraceSettings.color == OnOffTty::On) {
|
|
outro = "...\033[0m";
|
|
} else {
|
|
outro = "...";
|
|
}
|
|
write(fd, outro, strlen(outro));
|
|
#endif
|
|
}
|
|
|
|
} // namespace backtrace
|
|
} // namespace runtime
|
|
} // namespace swift
|