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 3 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
19 changes: 18 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,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
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
70 changes: 70 additions & 0 deletions include/swift/Runtime/CrashInfo.h
@@ -0,0 +1,70 @@
//===--- 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

struct CrashInfo {
uint64_t crashing_thread;
uint64_t signal;
uint64_t fault_address;

#ifdef __APPLE__
uint64_t mctx;
#elif defined(__linux__)
uint64_t thread_list;
#endif
};

#ifdef __linux__

struct memserver_req {
uint64_t addr;
uint64_t len;
};

struct memserver_resp {
uint64_t addr;
int64_t len;
al45tair marked this conversation as resolved.
Show resolved Hide resolved
/* Then len bytes of data */
};

struct thread {
uint64_t next;
int64_t tid;
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
161 changes: 157 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,109 @@ _swift_isThunkFunction(const char *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);
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';

*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 +967,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 +991,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