Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[Backtracing][Linux] Add Linux crash handler to the runtime. #66334

Merged
merged 5 commits into from Jun 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
46 changes: 45 additions & 1 deletion include/swift/Runtime/Backtrace.h
Expand Up @@ -17,10 +17,17 @@
#ifndef 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/CrashInfo.h"

#include "swift/shims/Visibility.h"
#include "swift/shims/CrashInfo.h"

#include <inttypes.h>

Expand Down Expand Up @@ -50,7 +57,11 @@ typedef int ErrorCode;

SWIFT_RUNTIME_STDLIB_INTERNAL ErrorCode _swift_installCrashHandler();

#ifdef __linux__
SWIFT_RUNTIME_STDLIB_INTERNAL bool _swift_spawnBacktracer(const ArgChar * const *argv, int memserver_fd);
al45tair marked this conversation as resolved.
Show resolved Hide resolved
#else
SWIFT_RUNTIME_STDLIB_INTERNAL bool _swift_spawnBacktracer(const ArgChar * const *argv);
#endif

enum class UnwindAlgorithm {
Auto = 0,
Expand Down Expand Up @@ -125,6 +136,39 @@ SWIFT_RUNTIME_STDLIB_INTERNAL BacktraceSettings _swift_backtraceSettings;

SWIFT_RUNTIME_STDLIB_SPI SWIFT_CC(swift) bool _swift_isThunkFunction(const char *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 contains the size of the
/// output buffer.
/// @param status returns the status codes defined in the C++ ABI.
///
/// If outputBuffer is nullptr, the function will allocate memory for the
/// result using malloc(). In this case, outputBufferSize may be nullptr;
/// if it is *not* nullptr, it will be set to the size of buffer that was
/// allocated. This is not necessarily the length of the string (it may be
/// somewhat higher).
///
/// 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).
///
/// The unusual behaviour here is a consequence of the way the C++ ABI's
/// demangling function works.
///
/// @returns a pointer to the output 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);
#ifdef __cplusplus
} // namespace backtrace
} // namespace runtime
Expand Down
98 changes: 98 additions & 0 deletions include/swift/Runtime/CrashInfo.h
@@ -0,0 +1,98 @@
//===--- CrashInfo.h - Swift Backtracing Crash Information ------*- 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 the CrashInfo type that holds information about why the program
// crashed.
//
//===----------------------------------------------------------------------===//

#ifndef SWIFT_CRASHINFO_H
#define SWIFT_CRASHINFO_H

#include <inttypes.h>

#ifdef __cplusplus
namespace swift {
namespace runtime {
namespace backtrace {
extern "C" {
#endif

// Note: The "pointers" below are pointers in a different process's address
// space, which might not even share our bitness. That is why they are
// `uint64_t`s, rather than pointers or `uintptr_t`s.

// The address of this structure in memory is passed to swift-backtrace.
struct CrashInfo {
// The thread ID for the crashing thread.
uint64_t crashing_thread;

// The signal number corresponding to this crash.
uint64_t signal;

// The fault address.
uint64_t fault_address;

#ifdef __APPLE__
// Points to the mcontext_t structure for the crashing thread; other
// threads' contexts can be recovered using Mach APIs later.
uint64_t mctx;
#elif defined(__linux__)
// The head of the thread list; points at a "struct thread" (see below).
uint64_t thread_list;
#endif
};

#ifdef __linux__

// A memory server request packet.
struct memserver_req {
// Address to read.
uint64_t addr;

// Number of bytes to read.
uint64_t len;
};

// A memory server response packet.
struct memserver_resp {
// Address that was read from.
uint64_t addr;

// Number of bytes, *or* negative to indicate an error.
int64_t len;
al45tair marked this conversation as resolved.
Show resolved Hide resolved

// Followed by len bytes of data if len > 0
};

// Holds details of a running thread.
struct thread {
// Points at the next thread.
uint64_t next;

// The thread ID for this thread.
int64_t tid;

// Points to the Linux ucontext_t structure.
uint64_t uctx;
};

#endif

#ifdef __cplusplus
} // extern "C"
} // namespace backtrace
} // namespace runtime
} // namespace swift
#endif

#endif // SWIFT_CRASHINFO_H
3 changes: 3 additions & 0 deletions include/swift/Runtime/Debug.h
Expand Up @@ -124,6 +124,9 @@ swift_dynamicCastFailure(const void *sourceType, const char *sourceName,
SWIFT_RUNTIME_EXPORT
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().
SWIFT_RUNTIME_ATTRIBUTE_NORETURN SWIFT_RUNTIME_ATTRIBUTE_NOINLINE
void swift_abortRetainOverflow();
Expand Down
145 changes: 141 additions & 4 deletions stdlib/public/runtime/Backtrace.cpp
Expand Up @@ -52,6 +52,12 @@
#include <cstring>
#include <cerrno>

#ifdef _WIN32
// We'll probably want dbghelp.h here
#else
#include <cxxabi.h>
#endif

#define DEBUG_BACKTRACING_SETTINGS 0

#ifndef lengthof
Expand Down Expand Up @@ -773,6 +779,54 @@ _swift_backtraceSetupEnvironment()
*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 */
al45tair marked this conversation as resolved.
Show resolved Hide resolved
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(). */
al45tair marked this conversation as resolved.
Show resolved Hide resolved
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
Expand All @@ -796,13 +850,93 @@ _swift_isThunkFunction(const char *mangledName) {
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,
int *status) {
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);
al45tair marked this conversation as resolved.
Show resolved Hide resolved
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) {
al45tair marked this conversation as resolved.
Show resolved Hide resolved
return result;
}

size_t toCopy = std::min(bufferSize - 1, resultLen - 1);
al45tair marked this conversation as resolved.
Show resolved Hide resolved
::memcpy(outputBuffer, result, toCopy);
outputBuffer[toCopy] = '\0';

free(result);

*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
// 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 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;
const char *env[BACKTRACE_MAX_ENV_VARS + 1];

Expand All @@ -817,10 +951,16 @@ _swift_spawnBacktracer(const ArgChar * const *argv)

// 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;

Expand All @@ -835,10 +975,7 @@ _swift_spawnBacktracer(const ArgChar * const *argv)

return false;

// ###TODO: Linux
// ###TODO: Windows
#else
return false;
#endif
}

Expand Down
3 changes: 2 additions & 1 deletion stdlib/public/runtime/CMakeLists.txt
Expand Up @@ -81,7 +81,8 @@ set(swift_runtime_sources

set(swift_runtime_backtracing_sources
Backtrace.cpp
CrashHandlerMacOS.cpp)
CrashHandlerMacOS.cpp
CrashHandlerLinux.cpp)

# Acknowledge that the following sources are known.
set(LLVM_OPTIONAL_SOURCES
Expand Down