mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[Backtracing][Linux] Add Linux crash handler to the runtime.
This also adds a function to demangle a symbol, and a way for the backtracing code to report warning messages to the same place as the main runtime. I'd like to rename the _swift_isThunkFunction() SPI also, but we can't do that until we've made the changes to the _Backtracing library, so we'll do that there instead. rdar://110261430
This commit is contained in:
@@ -17,10 +17,17 @@
|
|||||||
#ifndef SWIFT_RUNTIME_BACKTRACE_H
|
#ifndef SWIFT_RUNTIME_BACKTRACE_H
|
||||||
#define SWIFT_RUNTIME_BACKTRACE_H
|
#define SWIFT_RUNTIME_BACKTRACE_H
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
#include <signal.h>
|
||||||
|
#endif // defined(__linux__)
|
||||||
|
|
||||||
#include "swift/Runtime/Config.h"
|
#include "swift/Runtime/Config.h"
|
||||||
|
#include "swift/Runtime/CrashInfo.h"
|
||||||
|
|
||||||
#include "swift/shims/Visibility.h"
|
#include "swift/shims/Visibility.h"
|
||||||
#include "swift/shims/CrashInfo.h"
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
|
||||||
@@ -50,7 +57,11 @@ typedef int ErrorCode;
|
|||||||
|
|
||||||
SWIFT_RUNTIME_STDLIB_INTERNAL ErrorCode _swift_installCrashHandler();
|
SWIFT_RUNTIME_STDLIB_INTERNAL ErrorCode _swift_installCrashHandler();
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
SWIFT_RUNTIME_STDLIB_INTERNAL bool _swift_spawnBacktracer(const ArgChar * const *argv, int memserver_fd);
|
||||||
|
#else
|
||||||
SWIFT_RUNTIME_STDLIB_INTERNAL bool _swift_spawnBacktracer(const ArgChar * const *argv);
|
SWIFT_RUNTIME_STDLIB_INTERNAL bool _swift_spawnBacktracer(const ArgChar * const *argv);
|
||||||
|
#endif
|
||||||
|
|
||||||
enum class UnwindAlgorithm {
|
enum class UnwindAlgorithm {
|
||||||
Auto = 0,
|
Auto = 0,
|
||||||
@@ -125,6 +136,12 @@ SWIFT_RUNTIME_STDLIB_INTERNAL BacktraceSettings _swift_backtraceSettings;
|
|||||||
|
|
||||||
SWIFT_RUNTIME_STDLIB_SPI SWIFT_CC(swift) bool _swift_isThunkFunction(const char *mangledName);
|
SWIFT_RUNTIME_STDLIB_SPI SWIFT_CC(swift) bool _swift_isThunkFunction(const char *mangledName);
|
||||||
|
|
||||||
|
SWIFT_RUNTIME_STDLIB_SPI
|
||||||
|
char *_swift_backtrace_demangle(const char *mangledName,
|
||||||
|
size_t mangledNameLength,
|
||||||
|
char *outputBuffer,
|
||||||
|
size_t *outputBufferSize,
|
||||||
|
int *status);
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} // namespace backtrace
|
} // namespace backtrace
|
||||||
} // namespace runtime
|
} // namespace runtime
|
||||||
|
|||||||
@@ -124,6 +124,9 @@ swift_dynamicCastFailure(const void *sourceType, const char *sourceName,
|
|||||||
SWIFT_RUNTIME_EXPORT
|
SWIFT_RUNTIME_EXPORT
|
||||||
void swift_reportError(uint32_t flags, const char *message);
|
void swift_reportError(uint32_t flags, const char *message);
|
||||||
|
|
||||||
|
SWIFT_RUNTIME_EXPORT
|
||||||
|
void swift_reportWarning(uint32_t flags, const char *message);
|
||||||
|
|
||||||
// Halt due to an overflow in swift_retain().
|
// Halt due to an overflow in swift_retain().
|
||||||
SWIFT_RUNTIME_ATTRIBUTE_NORETURN SWIFT_RUNTIME_ATTRIBUTE_NOINLINE
|
SWIFT_RUNTIME_ATTRIBUTE_NORETURN SWIFT_RUNTIME_ATTRIBUTE_NOINLINE
|
||||||
void swift_abortRetainOverflow();
|
void swift_abortRetainOverflow();
|
||||||
|
|||||||
@@ -52,6 +52,12 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
// We'll probably want dbghelp.h here
|
||||||
|
#else
|
||||||
|
#include <cxxabi.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#define DEBUG_BACKTRACING_SETTINGS 0
|
#define DEBUG_BACKTRACING_SETTINGS 0
|
||||||
|
|
||||||
#ifndef lengthof
|
#ifndef lengthof
|
||||||
@@ -70,7 +76,7 @@ SWIFT_RUNTIME_STDLIB_INTERNAL BacktraceSettings _swift_backtraceSettings = {
|
|||||||
// enabled
|
// enabled
|
||||||
#if TARGET_OS_OSX
|
#if TARGET_OS_OSX
|
||||||
OnOffTty::TTY,
|
OnOffTty::TTY,
|
||||||
#elif 0 // defined(__linux__) || defined(_WIN32)
|
#elif defined(__linux__) // || defined(_WIN32)
|
||||||
OnOffTty::On,
|
OnOffTty::On,
|
||||||
#else
|
#else
|
||||||
OnOffTty::Off,
|
OnOffTty::Off,
|
||||||
@@ -80,7 +86,7 @@ SWIFT_RUNTIME_STDLIB_INTERNAL BacktraceSettings _swift_backtraceSettings = {
|
|||||||
true,
|
true,
|
||||||
|
|
||||||
// interactive
|
// interactive
|
||||||
#if TARGET_OS_OSX // || defined(__linux__) || defined(_WIN32)
|
#if TARGET_OS_OSX || defined(__linux__) // || defined(_WIN32)
|
||||||
OnOffTty::TTY,
|
OnOffTty::TTY,
|
||||||
#else
|
#else
|
||||||
OnOffTty::Off,
|
OnOffTty::Off,
|
||||||
@@ -773,6 +779,54 @@ _swift_backtraceSetupEnvironment()
|
|||||||
*penv = 0;
|
*penv = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
struct spawn_info {
|
||||||
|
const char *path;
|
||||||
|
char * const *argv;
|
||||||
|
char * const *envp;
|
||||||
|
int memserver;
|
||||||
|
};
|
||||||
|
|
||||||
|
uint8_t spawn_stack[4096];
|
||||||
|
|
||||||
|
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
|
#endif // SWIFT_BACKTRACE_ON_CRASH_SUPPORTED
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
@@ -796,13 +850,109 @@ _swift_isThunkFunction(const char *mangledName) {
|
|||||||
return ctx.isThunkSymbol(mangledName);
|
return ctx.isThunkSymbol(mangledName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Try to demangle a symbol.
|
||||||
|
///
|
||||||
|
/// Unlike other entry points that do this, we try both Swift and C++ here.
|
||||||
|
///
|
||||||
|
/// @param mangledName is the symbol name to be demangled.
|
||||||
|
/// @param mangledNameLength is the length of this name.
|
||||||
|
/// @param outputBuffer is a pointer to a buffer in which to place the result.
|
||||||
|
/// @param outputBufferSize points to a variable that will be filled in with
|
||||||
|
/// the size of the allocated buffer (NOT the length of the result).
|
||||||
|
/// @param status returns the status codes defined in the C++ ABI.
|
||||||
|
///
|
||||||
|
/// If outputBuffer and outputBufferSize are both nullptr, this function will
|
||||||
|
/// allocate memory for the result using malloc().
|
||||||
|
///
|
||||||
|
/// If outputBuffer is nullptr but outputBufferSize is not, the function will
|
||||||
|
/// fill outputBufferSize with the required buffer size and return nullptr.
|
||||||
|
///
|
||||||
|
/// Otherwise, the result will be written into the output buffer, and the
|
||||||
|
/// size of the result will be written into outputBufferSize. If the buffer
|
||||||
|
/// is too small, the result will be truncated, but outputBufferSize will
|
||||||
|
/// still be set to the number of bytes that would have been required to
|
||||||
|
/// copy out the full result (including a trailing NUL).
|
||||||
|
///
|
||||||
|
/// @returns `true` if demangling was successful.
|
||||||
|
SWIFT_RUNTIME_STDLIB_SPI char *
|
||||||
|
_swift_backtrace_demangle(const char *mangledName,
|
||||||
|
size_t mangledNameLength,
|
||||||
|
char *outputBuffer,
|
||||||
|
size_t *outputBufferSize,
|
||||||
|
int *status) {
|
||||||
|
llvm::StringRef name = llvm::StringRef(mangledName, mangledNameLength);
|
||||||
|
|
||||||
|
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';
|
||||||
|
|
||||||
|
*status = 0;
|
||||||
|
return outputBuffer;
|
||||||
|
#ifndef _WIN32
|
||||||
|
} else if (name.startswith("_Z")) {
|
||||||
|
// Try C++
|
||||||
|
size_t resultLen;
|
||||||
|
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';
|
||||||
|
|
||||||
|
*status = 0;
|
||||||
|
return outputBuffer;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// On Windows, the mangling is different.
|
||||||
|
// ###TODO: Call __unDName()
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
*status = -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
// N.B. THIS FUNCTION MUST BE SAFE TO USE FROM A CRASH HANDLER. On Linux
|
// 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
|
// and macOS, that means it must be async-signal-safe. On Windows, there
|
||||||
// isn't an equivalent notion but a similar restriction applies.
|
// isn't an equivalent notion but a similar restriction applies.
|
||||||
SWIFT_RUNTIME_STDLIB_INTERNAL bool
|
SWIFT_RUNTIME_STDLIB_INTERNAL bool
|
||||||
|
#ifdef __linux__
|
||||||
|
_swift_spawnBacktracer(const ArgChar * const *argv, int memserver_fd)
|
||||||
|
#else
|
||||||
_swift_spawnBacktracer(const ArgChar * const *argv)
|
_swift_spawnBacktracer(const ArgChar * const *argv)
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
#if TARGET_OS_OSX || TARGET_OS_MACCATALYST
|
#if !SWIFT_BACKTRACE_ON_CRASH_SUPPORTED
|
||||||
|
return false;
|
||||||
|
#elif TARGET_OS_OSX || TARGET_OS_MACCATALYST || defined(__linux__)
|
||||||
pid_t child;
|
pid_t child;
|
||||||
const char *env[BACKTRACE_MAX_ENV_VARS + 1];
|
const char *env[BACKTRACE_MAX_ENV_VARS + 1];
|
||||||
|
|
||||||
@@ -817,10 +967,16 @@ _swift_spawnBacktracer(const ArgChar * const *argv)
|
|||||||
|
|
||||||
// SUSv3 says argv and envp are "completely constant" and that the reason
|
// SUSv3 says argv and envp are "completely constant" and that the reason
|
||||||
// posix_spawn() et al use char * const * is for compatibility.
|
// 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,
|
int ret = posix_spawn(&child, swiftBacktracePath,
|
||||||
&backtraceFileActions, &backtraceSpawnAttrs,
|
&backtraceFileActions, &backtraceSpawnAttrs,
|
||||||
const_cast<char * const *>(argv),
|
const_cast<char * const *>(argv),
|
||||||
const_cast<char * const *>(env));
|
const_cast<char * const *>(env));
|
||||||
|
#endif
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -835,10 +991,7 @@ _swift_spawnBacktracer(const ArgChar * const *argv)
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// ###TODO: Linux
|
|
||||||
// ###TODO: Windows
|
// ###TODO: Windows
|
||||||
#else
|
|
||||||
return false;
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -81,7 +81,8 @@ set(swift_runtime_sources
|
|||||||
|
|
||||||
set(swift_runtime_backtracing_sources
|
set(swift_runtime_backtracing_sources
|
||||||
Backtrace.cpp
|
Backtrace.cpp
|
||||||
CrashHandlerMacOS.cpp)
|
CrashHandlerMacOS.cpp
|
||||||
|
CrashHandlerLinux.cpp)
|
||||||
|
|
||||||
# Acknowledge that the following sources are known.
|
# Acknowledge that the following sources are known.
|
||||||
set(LLVM_OPTIONAL_SOURCES
|
set(LLVM_OPTIONAL_SOURCES
|
||||||
|
|||||||
770
stdlib/public/runtime/CrashHandlerLinux.cpp
Normal file
770
stdlib/public/runtime/CrashHandlerLinux.cpp
Normal file
@@ -0,0 +1,770 @@
|
|||||||
|
//===--- CrashHandlerLinux.cpp - Swift crash handler for Linux ----------- ===//
|
||||||
|
//
|
||||||
|
// 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 Linux crash handler implementation.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
|
||||||
|
#include <linux/capability.h>
|
||||||
|
#include <linux/futex.h>
|
||||||
|
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/prctl.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/syscall.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/uio.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <sched.h>
|
||||||
|
#include <setjmp.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "swift/Runtime/Backtrace.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
// Run the memserver in a thread (0) or separate process (1)
|
||||||
|
#define MEMSERVER_USE_PROCESS 0
|
||||||
|
|
||||||
|
#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(struct thread *self);
|
||||||
|
void resume_other_threads();
|
||||||
|
void take_thread_lock();
|
||||||
|
void release_thread_lock();
|
||||||
|
void notify_paused();
|
||||||
|
void wait_paused(uint32_t expected, const struct timespec *timeout);
|
||||||
|
int memserver_start();
|
||||||
|
int memserver_entry(void *);
|
||||||
|
bool run_backtracer(int fd);
|
||||||
|
|
||||||
|
int safe_read(int fd, void *buf, size_t len) {
|
||||||
|
int ret;
|
||||||
|
do {
|
||||||
|
ret = read(fd, buf, len);
|
||||||
|
} while (ret < 0 && errno == EINTR);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int safe_write(int fd, const void *buf, size_t len) {
|
||||||
|
int ret;
|
||||||
|
do {
|
||||||
|
ret = write(fd, buf, len);
|
||||||
|
} while (ret < 0 && errno == EINTR);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
CrashInfo crashInfo;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
// See if an alternate signal stack already exists
|
||||||
|
if (sigaltstack(NULL, &ss) < 0)
|
||||||
|
return errno;
|
||||||
|
|
||||||
|
if (ss.ss_sp == 0) {
|
||||||
|
/* No, so set one up; note that if we end up having to do a PLT lookup
|
||||||
|
for a function we call from the signal handler, we need additional
|
||||||
|
stack space for the dynamic linker, or we'll just explode. That's
|
||||||
|
what the extra 16KB is for here. */
|
||||||
|
ss.ss_flags = 0;
|
||||||
|
ss.ss_size = SIGSTKSZ + 16384;
|
||||||
|
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;
|
||||||
|
sigfillset(&sa.sa_mask);
|
||||||
|
for (unsigned n = 0; n < lengthof(signalsToHandle); ++n) {
|
||||||
|
sigdelset(&sa.sa_mask, signalsToHandle[n]);
|
||||||
|
}
|
||||||
|
|
||||||
|
sa.sa_flags = SA_ONSTACK | SA_SIGINFO | SA_NODEFER;
|
||||||
|
sa.sa_sigaction = handle_fatal_signal;
|
||||||
|
|
||||||
|
for (unsigned n = 0; n < lengthof(signalsToHandle); ++n) {
|
||||||
|
struct sigaction osa;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace backtrace
|
||||||
|
} // namespace runtime
|
||||||
|
} // namespace swift
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
void
|
||||||
|
reset_signal(int signum)
|
||||||
|
{
|
||||||
|
struct sigaction sa;
|
||||||
|
sa.sa_handler = SIG_DFL;
|
||||||
|
sa.sa_flags = 0;
|
||||||
|
sigemptyset(&sa.sa_mask);
|
||||||
|
|
||||||
|
sigaction(signum, &sa, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
handle_fatal_signal(int signum,
|
||||||
|
siginfo_t *pinfo,
|
||||||
|
void *uctx)
|
||||||
|
{
|
||||||
|
int old_err = errno;
|
||||||
|
struct thread self = { 0, (int64_t)gettid(), (uint64_t)uctx };
|
||||||
|
|
||||||
|
// Prevent this from exploding if more than one thread gets here at once
|
||||||
|
suspend_other_threads(&self);
|
||||||
|
|
||||||
|
// Remove our signal handlers; crashes should kill us here
|
||||||
|
for (unsigned n = 0; n < lengthof(signalsToHandle); ++n)
|
||||||
|
reset_signal(signalsToHandle[n]);
|
||||||
|
|
||||||
|
// Fill in crash info
|
||||||
|
crashInfo.crashing_thread = self.tid;
|
||||||
|
crashInfo.signal = signum;
|
||||||
|
crashInfo.fault_address = (uint64_t)pinfo->si_addr;
|
||||||
|
|
||||||
|
// Start the memory server
|
||||||
|
int fd = memserver_start();
|
||||||
|
|
||||||
|
/* Start the backtracer; this will suspend the process, so there's no need
|
||||||
|
to try to suspend other threads from here. */
|
||||||
|
run_backtracer(fd);
|
||||||
|
|
||||||
|
#if !MEMSERVER_USE_PROCESS
|
||||||
|
/* If the memserver is in-process, it may have set signal handlers,
|
||||||
|
so reset SIGSEGV and SIGBUS again */
|
||||||
|
reset_signal(SIGSEGV);
|
||||||
|
reset_signal(SIGBUS);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Restart the other threads
|
||||||
|
resume_other_threads();
|
||||||
|
|
||||||
|
// Restore errno and exit (to crash)
|
||||||
|
errno = old_err;
|
||||||
|
}
|
||||||
|
|
||||||
|
// .. Thread handling ..........................................................
|
||||||
|
|
||||||
|
void
|
||||||
|
reset_threads(struct thread *first) {
|
||||||
|
__atomic_store_n(&crashInfo.thread_list, (uint64_t)first, __ATOMIC_RELEASE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
add_thread(struct thread *thread) {
|
||||||
|
uint64_t next = __atomic_load_n(&crashInfo.thread_list, __ATOMIC_ACQUIRE);
|
||||||
|
do {
|
||||||
|
thread->next = next;
|
||||||
|
} while (!__atomic_compare_exchange_n(&crashInfo.thread_list, &next,
|
||||||
|
(uint64_t)thread,
|
||||||
|
false,
|
||||||
|
__ATOMIC_RELEASE, __ATOMIC_ACQUIRE));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
seen_thread(pid_t tid) {
|
||||||
|
uint64_t next = __atomic_load_n(&crashInfo.thread_list, __ATOMIC_ACQUIRE);
|
||||||
|
while (next) {
|
||||||
|
struct thread *pthread = (struct thread *)next;
|
||||||
|
if (pthread->tid == tid)
|
||||||
|
return true;
|
||||||
|
next = pthread->next;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
pause_thread(int signum __attribute__((unused)),
|
||||||
|
siginfo_t *pinfo __attribute__((unused)),
|
||||||
|
void *uctx)
|
||||||
|
{
|
||||||
|
int old_err = errno;
|
||||||
|
struct thread self = { 0, (int64_t)gettid(), (uint64_t)uctx };
|
||||||
|
|
||||||
|
add_thread(&self);
|
||||||
|
|
||||||
|
notify_paused();
|
||||||
|
|
||||||
|
take_thread_lock();
|
||||||
|
release_thread_lock();
|
||||||
|
|
||||||
|
errno = old_err;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct linux_dirent64 {
|
||||||
|
ino64_t d_ino;
|
||||||
|
off64_t d_off;
|
||||||
|
unsigned short d_reclen;
|
||||||
|
unsigned char d_type;
|
||||||
|
char d_name[256];
|
||||||
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
getdents(int fd, void *buf, size_t bufsiz)
|
||||||
|
{
|
||||||
|
return syscall(SYS_getdents64, fd, buf, bufsiz);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stop all other threads in this process; we do this by establishing a
|
||||||
|
signal handler for SIGUSR1, then iterating through the threads sending
|
||||||
|
SIGUSR1.
|
||||||
|
|
||||||
|
Finding the other threads is a pain, because Linux has no actual API
|
||||||
|
for that; instead, you have to read /proc. Unfortunately, opendir()
|
||||||
|
and readdir() are not async signal safe, so we get to do this with
|
||||||
|
the getdents system call instead.
|
||||||
|
|
||||||
|
The SIGUSR1 signals also serve to build the thread list. */
|
||||||
|
void
|
||||||
|
suspend_other_threads(struct thread *self)
|
||||||
|
{
|
||||||
|
struct sigaction sa, sa_old;
|
||||||
|
|
||||||
|
// Take the lock
|
||||||
|
take_thread_lock();
|
||||||
|
|
||||||
|
// Start the thread list with this thread
|
||||||
|
reset_threads(self);
|
||||||
|
|
||||||
|
// Swap out the SIGPROF signal handler first
|
||||||
|
sigfillset(&sa.sa_mask);
|
||||||
|
sa.sa_flags = SA_NODEFER;
|
||||||
|
sa.sa_handler = NULL;
|
||||||
|
sa.sa_sigaction = pause_thread;
|
||||||
|
|
||||||
|
sigaction(SIGPROF, &sa, &sa_old);
|
||||||
|
|
||||||
|
/* Now scan /proc/self/task to get the tids of the threads in this
|
||||||
|
process. We need to ignore our own thread. */
|
||||||
|
int fd = open("/proc/self/task",
|
||||||
|
O_RDONLY|O_NDELAY|O_DIRECTORY|O_LARGEFILE|O_CLOEXEC);
|
||||||
|
int our_pid = getpid();
|
||||||
|
char buffer[4096];
|
||||||
|
size_t offset = 0;
|
||||||
|
size_t count = 0;
|
||||||
|
|
||||||
|
uint32_t thread_count = 0;
|
||||||
|
uint32_t old_thread_count;
|
||||||
|
|
||||||
|
do {
|
||||||
|
old_thread_count = thread_count;
|
||||||
|
lseek(fd, 0, SEEK_SET);
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
if (offset >= count) {
|
||||||
|
ssize_t bytes = getdents(fd, buffer, sizeof(buffer));
|
||||||
|
if (bytes <= 0)
|
||||||
|
break;
|
||||||
|
count = (size_t)bytes;
|
||||||
|
offset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct linux_dirent64 *dp = (struct linux_dirent64 *)&buffer[offset];
|
||||||
|
offset += dp->d_reclen;
|
||||||
|
|
||||||
|
if (strcmp(dp->d_name, ".") == 0
|
||||||
|
|| strcmp(dp->d_name, "..") == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int tid = atoi(dp->d_name);
|
||||||
|
|
||||||
|
if ((int64_t)tid != self->tid && !seen_thread(tid)) {
|
||||||
|
tgkill(our_pid, tid, SIGPROF);
|
||||||
|
++thread_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait up to 5 seconds for the threads to pause
|
||||||
|
struct timespec timeout = { 5, 0 };
|
||||||
|
wait_paused(thread_count, &timeout);
|
||||||
|
} while (old_thread_count != thread_count);
|
||||||
|
|
||||||
|
// Close the directory
|
||||||
|
close(fd);
|
||||||
|
|
||||||
|
// Finally, reset the signal handler
|
||||||
|
sigaction(SIGPROF, &sa_old, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
resume_other_threads()
|
||||||
|
{
|
||||||
|
// All we need to do here is release the lock.
|
||||||
|
release_thread_lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
// .. Locking ..................................................................
|
||||||
|
|
||||||
|
/* We use a futex to block the threads; we also use one to let us work out
|
||||||
|
when all the threads we've asked to pause have actually paused. */
|
||||||
|
int
|
||||||
|
futex(uint32_t *uaddr, int futex_op, uint32_t val,
|
||||||
|
const struct timespec *timeout, uint32_t *uaddr2, uint32_t val3)
|
||||||
|
{
|
||||||
|
return syscall(SYS_futex, uaddr, futex_op, val, timeout, uaddr2, val3);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t thread_lock = 0;
|
||||||
|
|
||||||
|
void
|
||||||
|
take_thread_lock()
|
||||||
|
{
|
||||||
|
do {
|
||||||
|
uint32_t zero = 0;
|
||||||
|
if (__atomic_compare_exchange_n(&thread_lock,
|
||||||
|
&zero,
|
||||||
|
1,
|
||||||
|
true,
|
||||||
|
__ATOMIC_ACQUIRE,
|
||||||
|
__ATOMIC_RELAXED))
|
||||||
|
return;
|
||||||
|
} while (!futex(&thread_lock, FUTEX_WAIT, 1, NULL, NULL, 0)
|
||||||
|
|| errno == EAGAIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
release_thread_lock()
|
||||||
|
{
|
||||||
|
__atomic_store_n(&thread_lock, 0, __ATOMIC_RELEASE);
|
||||||
|
futex(&thread_lock, FUTEX_WAKE, 1, NULL, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t threads_paused = 0;
|
||||||
|
|
||||||
|
void
|
||||||
|
notify_paused()
|
||||||
|
{
|
||||||
|
__atomic_fetch_add(&threads_paused, 1, __ATOMIC_RELEASE);
|
||||||
|
futex(&threads_paused, FUTEX_WAKE, 1, NULL, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
wait_paused(uint32_t expected, const struct timespec *timeout)
|
||||||
|
{
|
||||||
|
uint32_t current;
|
||||||
|
do {
|
||||||
|
current = __atomic_load_n(&threads_paused, __ATOMIC_ACQUIRE);
|
||||||
|
if (current == expected)
|
||||||
|
return;
|
||||||
|
} while (!futex(&threads_paused, FUTEX_WAIT, current, timeout, NULL, 0)
|
||||||
|
|| errno == EAGAIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
// .. Memory server ............................................................
|
||||||
|
|
||||||
|
/* The memory server exists so that we can gain access to the crashing
|
||||||
|
process's memory space from the backtracer without having to use ptrace()
|
||||||
|
or process_vm_readv(), both of which need CAP_SYS_PTRACE.
|
||||||
|
|
||||||
|
We don't want to require CAP_SYS_PTRACE because we're potentially being
|
||||||
|
used inside of a Docker container, which won't have that enabled. */
|
||||||
|
|
||||||
|
char memserver_stack[4096];
|
||||||
|
char memserver_buffer[4096];
|
||||||
|
int memserver_fd;
|
||||||
|
bool memserver_has_ptrace;
|
||||||
|
sigjmp_buf memserver_fault_buf;
|
||||||
|
pid_t memserver_pid;
|
||||||
|
|
||||||
|
int
|
||||||
|
memserver_start()
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
int fds[2];
|
||||||
|
|
||||||
|
ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
memserver_fd = fds[0];
|
||||||
|
ret = clone(memserver_entry, memserver_stack + sizeof(memserver_stack),
|
||||||
|
#if MEMSERVER_USE_PROCESS
|
||||||
|
0,
|
||||||
|
#else
|
||||||
|
CLONE_THREAD | CLONE_VM | CLONE_FILES
|
||||||
|
| CLONE_FS | CLONE_IO | CLONE_SIGHAND,
|
||||||
|
#endif
|
||||||
|
NULL);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
#if MEMSERVER_USE_PROCESS
|
||||||
|
memserver_pid = ret;
|
||||||
|
|
||||||
|
/* Tell the Yama LSM module, if it's running, that it's OK for
|
||||||
|
the memserver to read process memory */
|
||||||
|
prctl(PR_SET_PTRACER, ret);
|
||||||
|
|
||||||
|
close(fds[0]);
|
||||||
|
#else
|
||||||
|
memserver_pid = getpid();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return fds[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
memserver_fault(int sig) {
|
||||||
|
(void)sig;
|
||||||
|
siglongjmp(memserver_fault_buf, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
ssize_t __attribute__((noinline))
|
||||||
|
memserver_read(void *to, const void *from, size_t len) {
|
||||||
|
if (memserver_has_ptrace) {
|
||||||
|
struct iovec local = { to, len };
|
||||||
|
struct iovec remote = { const_cast<void *>(from), len };
|
||||||
|
return process_vm_readv(memserver_pid, &local, 1, &remote, 1, 0);
|
||||||
|
} else {
|
||||||
|
if (!sigsetjmp(memserver_fault_buf, 1)) {
|
||||||
|
memcpy(to, from, len);
|
||||||
|
return len;
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
memserver_entry(void *dummy __attribute__((unused))) {
|
||||||
|
int fd = memserver_fd;
|
||||||
|
int result = 1;
|
||||||
|
|
||||||
|
#if MEMSERVER_USE_PROCESS
|
||||||
|
prctl(PR_SET_NAME, "[backtrace]");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
memserver_has_ptrace = !!prctl(PR_CAPBSET_READ, CAP_SYS_PTRACE);
|
||||||
|
|
||||||
|
if (!memserver_has_ptrace) {
|
||||||
|
struct sigaction sa;
|
||||||
|
sigfillset(&sa.sa_mask);
|
||||||
|
sa.sa_handler = memserver_fault;
|
||||||
|
sa.sa_flags = SA_NODEFER;
|
||||||
|
sigaction(SIGSEGV, &sa, NULL);
|
||||||
|
sigaction(SIGBUS, &sa, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
struct memserver_req req;
|
||||||
|
ssize_t ret;
|
||||||
|
|
||||||
|
ret = safe_read(fd, &req, sizeof(req));
|
||||||
|
if (ret != sizeof(req))
|
||||||
|
break;
|
||||||
|
|
||||||
|
uint64_t addr = req.addr;
|
||||||
|
uint64_t bytes = req.len;
|
||||||
|
|
||||||
|
while (bytes) {
|
||||||
|
uint64_t todo = (bytes < sizeof(memserver_buffer)
|
||||||
|
? bytes : sizeof(memserver_buffer));
|
||||||
|
|
||||||
|
ret = memserver_read(memserver_buffer, (void *)addr, (size_t)todo);
|
||||||
|
|
||||||
|
struct memserver_resp resp;
|
||||||
|
|
||||||
|
resp.addr = addr;
|
||||||
|
resp.len = ret;
|
||||||
|
|
||||||
|
ret = safe_write(fd, &resp, sizeof(resp));
|
||||||
|
if (ret != sizeof(resp))
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
if (resp.len < 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
ret = safe_write(fd, memserver_buffer, resp.len);
|
||||||
|
if (ret != resp.len)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
addr += resp.len;
|
||||||
|
bytes -= resp.len;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result = 0;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
close(fd);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// .. Starting the backtracer ..................................................
|
||||||
|
|
||||||
|
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
|
||||||
|
"--cache", // 27
|
||||||
|
"true", // 28
|
||||||
|
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<uintptr_t>(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(int memserver_fd)
|
||||||
|
{
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
backtracer_argv[28] = trueOrFalse(_swift_backtraceSettings.cache);
|
||||||
|
|
||||||
|
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, memserver_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
#endif // __linux__
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ void suspend_other_threads();
|
|||||||
void resume_other_threads();
|
void resume_other_threads();
|
||||||
bool run_backtracer(void);
|
bool run_backtracer(void);
|
||||||
|
|
||||||
swift::CrashInfo crashInfo;
|
CrashInfo crashInfo;
|
||||||
|
|
||||||
os_unfair_lock crashLock = OS_UNFAIR_LOCK_INIT;
|
os_unfair_lock crashLock = OS_UNFAIR_LOCK_INIT;
|
||||||
|
|
||||||
|
|||||||
@@ -404,6 +404,12 @@ swift::warning(uint32_t flags, const char *format, ...)
|
|||||||
warningv(flags, format, args);
|
warningv(flags, format, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Report a warning to the system console and stderr. This is exported,
|
||||||
|
/// unlike the swift::warning() function above.
|
||||||
|
void swift::swift_reportWarning(uint32_t flags, const char *message) {
|
||||||
|
warning(flags, "%s", message);
|
||||||
|
}
|
||||||
|
|
||||||
// Crash when a deleted method is called by accident.
|
// Crash when a deleted method is called by accident.
|
||||||
SWIFT_RUNTIME_EXPORT SWIFT_NORETURN void swift_deletedMethodError() {
|
SWIFT_RUNTIME_EXPORT SWIFT_NORETURN void swift_deletedMethodError() {
|
||||||
swift::fatalError(/* flags = */ 0,
|
swift::fatalError(/* flags = */ 0,
|
||||||
|
|||||||
Reference in New Issue
Block a user