Skip to content

Commit

Permalink
Merge pull request #66334 from al45tair/eng/PR-110261430
Browse files Browse the repository at this point in the history
[Backtracing][Linux] Add Linux crash handler to the runtime.
  • Loading branch information
al45tair committed Jun 7, 2023
2 parents fe6f8f5 + 979b749 commit ae9a0c1
Show file tree
Hide file tree
Showing 8 changed files with 1,087 additions and 7 deletions.
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);
#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;

// 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 */
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
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);
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';

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

0 comments on commit ae9a0c1

Please sign in to comment.