[Backtracing] Add an indication that we're working on a backtrace.

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
This commit is contained in:
Alastair Houghton
2023-11-07 15:33:30 +00:00
parent 511c46ff62
commit 9d462a7b69
9 changed files with 396 additions and 229 deletions

View File

@@ -31,114 +31,17 @@
#include <inttypes.h>
#ifdef _WIN32
// For DWORD
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <windows.h>
// For wchar_t
#include <cwchar>
#endif
#ifdef __cplusplus
namespace swift {
namespace runtime {
namespace backtrace {
#endif
#ifdef _WIN32
typedef wchar_t ArgChar;
typedef DWORD ErrorCode;
#else
typedef char ArgChar;
typedef int ErrorCode;
#endif
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,
Fast = 1,
Precise = 2
};
enum class OnOffTty {
Off = 0,
On = 1,
TTY = 2
};
enum class Preset {
Auto = -1,
Friendly = 0,
Medium = 1,
Full = 2
};
enum class ThreadsToShow {
Preset = -1,
All = 0,
Crashed = 1
};
enum class RegistersToShow {
Preset = -1,
None = 0,
All = 1,
Crashed = 2
};
enum class ImagesToShow {
Preset = -1,
None = 0,
All = 1,
Mentioned = 2
};
enum class SanitizePaths {
Preset = -1,
Off = 0,
On = 1
};
enum class OutputTo {
Auto = -1,
Stdout = 0,
Stderr = 2,
};
struct BacktraceSettings {
UnwindAlgorithm algorithm;
OnOffTty enabled;
bool demangle;
OnOffTty interactive;
OnOffTty color;
unsigned timeout;
ThreadsToShow threads;
RegistersToShow registers;
ImagesToShow images;
unsigned limit;
unsigned top;
SanitizePaths sanitize;
Preset preset;
bool cache;
OutputTo outputTo;
const char *swiftBacktracePath;
};
SWIFT_RUNTIME_STDLIB_INTERNAL BacktraceSettings _swift_backtraceSettings;
inline bool _swift_backtrace_isEnabled() {
return _swift_backtraceSettings.enabled == OnOffTty::On;
}
// Test if a given function is a Swift thunk of some sort.
//
// @param mangledName is the name of the symbol to test.
//
// @returns true if the function is a thunk.
SWIFT_RUNTIME_STDLIB_SPI
bool _swift_backtrace_isThunkFunction(const char *mangledName);

View File

@@ -113,6 +113,38 @@ internal struct SwiftBacktrace {
}
}
static func subtract(timespec ts: timespec, from: timespec) -> timespec {
var sec = from.tv_sec - ts.tv_sec
var nsec = from.tv_nsec - ts.tv_nsec
if nsec < 0 {
sec -= 1
nsec += 1000000000
}
return timespec(tv_sec: sec, tv_nsec: nsec)
}
// We can't use Foundation here, so there's no String(format:, ...)
static func format(duration: timespec) -> String {
let centisRounded = (duration.tv_nsec + 5000000) / 10000000
let centis = centisRounded % 100
let secs = duration.tv_sec + (centisRounded / 100)
let d1 = centis / 10
let d2 = centis % 10
return "\(secs).\(d1)\(d2)"
}
static func measureDuration(_ body: () -> ()) -> timespec {
var startTime = timespec()
var endTime = timespec()
clock_gettime(CLOCK_MONOTONIC, &startTime)
body()
clock_gettime(CLOCK_MONOTONIC, &endTime)
return subtract(timespec: startTime, from: endTime)
}
static func usage() {
print("""
usage: swift-backtrace [--unwind <algorithm>] [--demangle [<bool>]] [--interactive [<bool>]] [--color [<bool>]] [--timeout <seconds>] [--preset <preset>] [--threads [<bool>]] [--registers <registers>] [--images <images>] [--cache [<bool>]] [--output-to <stream>] --crashinfo <addr>
@@ -460,16 +492,25 @@ Generate a backtrace for the parent process.
// want to do it *once* for all the backtraces we showed.
formattingOptions = formattingOptions.showImages(.none)
target = Target(crashInfoAddr: crashInfoAddr,
limit: args.limit, top: args.top,
cache: args.cache)
// Target's initializer fetches and symbolicates backtraces, so
// we want to time that part here.
let duration = measureDuration {
target = Target(crashInfoAddr: crashInfoAddr,
limit: args.limit, top: args.top,
cache: args.cache)
currentThread = target!.crashingThreadNdx
currentThread = target!.crashingThreadNdx
}
printCrashLog()
writeln("")
let formattedDuration = format(duration: duration)
writeln("Backtrace took \(formattedDuration)s")
writeln("")
if args.interactive {
// Make sure we're line buffered
setvbuf(stdout, nil, _IOLBF, 0)
@@ -611,7 +652,13 @@ Generate a backtrace for the parent process.
description = "Program crashed: \(target.signalDescription) at \(hex(target.faultAddress))"
}
writeln("")
// Clear the message written by the crash handler
if args.color {
write("\r\u{1b}[0K")
} else {
write("\r" + String(repeating:" ", count: 80) + "\r")
}
writeln(theme.crashReason(description))
var mentionedImages = Set<Int>()

View File

@@ -58,6 +58,8 @@
#include <cxxabi.h>
#endif
#include "BacktracePrivate.h"
#define DEBUG_BACKTRACING_SETTINGS 0
#ifndef lengthof
@@ -987,6 +989,57 @@ _swift_spawnBacktracer(const ArgChar * const *argv)
#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

View File

@@ -0,0 +1,147 @@
//===--- BacktraceUtils.h - Private backtracing utilities -------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 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
//
//===----------------------------------------------------------------------===//
//
// Private declarations of the Swift runtime's backtracing code.
//
//===----------------------------------------------------------------------===//
#ifndef SWIFT_RUNTIME_BACKTRACE_UTILS_H
#define SWIFT_RUNTIME_BACKTRACE_UTILS_H
#include "swift/Runtime/Config.h"
#include "swift/shims/Visibility.h"
#include <inttypes.h>
#ifdef _WIN32
// For DWORD
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <windows.h>
// For wchar_t
#include <cwchar>
#endif
namespace swift {
namespace runtime {
namespace backtrace {
#ifdef _WIN32
typedef wchar_t ArgChar;
typedef DWORD ErrorCode;
#else
typedef char ArgChar;
typedef int ErrorCode;
#endif
enum class UnwindAlgorithm {
Auto = 0,
Fast = 1,
Precise = 2
};
enum class OnOffTty {
Off = 0,
On = 1,
TTY = 2
};
enum class Preset {
Auto = -1,
Friendly = 0,
Medium = 1,
Full = 2
};
enum class ThreadsToShow {
Preset = -1,
All = 0,
Crashed = 1
};
enum class RegistersToShow {
Preset = -1,
None = 0,
All = 1,
Crashed = 2
};
enum class ImagesToShow {
Preset = -1,
None = 0,
All = 1,
Mentioned = 2
};
enum class SanitizePaths {
Preset = -1,
Off = 0,
On = 1
};
enum class OutputTo {
Auto = -1,
Stdout = 0,
Stderr = 2,
};
struct BacktraceSettings {
UnwindAlgorithm algorithm;
OnOffTty enabled;
bool demangle;
OnOffTty interactive;
OnOffTty color;
unsigned timeout;
ThreadsToShow threads;
RegistersToShow registers;
ImagesToShow images;
unsigned limit;
unsigned top;
SanitizePaths sanitize;
Preset preset;
bool cache;
OutputTo outputTo;
const char *swiftBacktracePath;
};
SWIFT_RUNTIME_STDLIB_INTERNAL BacktraceSettings _swift_backtraceSettings;
inline bool _swift_backtrace_isEnabled() {
return _swift_backtraceSettings.enabled == OnOffTty::On;
}
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
SWIFT_RUNTIME_STDLIB_INTERNAL void _swift_displayCrashMessage(int signum, const void *pc);
SWIFT_RUNTIME_STDLIB_INTERNAL
void _swift_formatAddress(uintptr_t addr, char buffer[18]);
inline void _swift_formatAddress(const void *ptr, char buffer[18]) {
_swift_formatAddress(reinterpret_cast<uintptr_t>(ptr), buffer);
}
SWIFT_RUNTIME_STDLIB_INTERNAL
void _swift_formatUnsigned(unsigned u, char buffer[22]);
} // namespace backtrace
} // namespace runtime
} // namespace swift
#endif // SWIFT_RUNTIME_BACKTRACE_UTILS_H

View File

@@ -0,0 +1,74 @@
//===--- BacktraceUtils.cpp - Private backtracing utilities -----*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 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 some async-signal-safe formatting functions.
//
//===----------------------------------------------------------------------===//
#include "BacktracePrivate.h"
namespace swift {
namespace runtime {
namespace backtracing {
// We can't call sprintf() here because we're in a signal handler,
// so we need to be async-signal-safe.
SWIFT_RUNTIME_STDLIB_INTERNAL void
_swift_formatAddress(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';
}
}
// See above; we can't use sprintf() here.
SWIFT_RUNTIME_STDLIB_INTERNAL void
_swift_formatUnsigned(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';
}
}
} // namespace backtracing
} // namespace runtime
} // namespace swift

View File

@@ -88,6 +88,7 @@ set(swift_runtime_threading_sources
set(swift_runtime_backtracing_sources
Backtrace.cpp
BacktraceUtils.cpp
CrashHandlerMacOS.cpp
CrashHandlerLinux.cpp)

View File

@@ -45,6 +45,8 @@
#include <cstring>
#include "BacktracePrivate.h"
// Run the memserver in a thread (0) or separate process (1)
#define MEMSERVER_USE_PROCESS 0
@@ -67,7 +69,6 @@ void wait_paused(uint32_t expected, const struct timespec *timeout);
int memserver_start();
int memserver_entry(void *);
bool run_backtracer(int fd);
void format_unsigned(unsigned u, char buffer[22]);
ssize_t safe_read(int fd, void *buf, size_t len) {
uint8_t *ptr = (uint8_t *)buf;
@@ -231,8 +232,30 @@ handle_fatal_signal(int signum,
// Start the memory server
int fd = memserver_start();
// Display a progress message
void *pc = 0;
ucontext_t *ctx = (ucontext_t *)uctx;
#if defined(__x86_64__)
pc = (void *)(ctx->uc_mcontext.gregs[REG_RIP]);
#elif defined(__i386__)
pc = (void *)(ctx->uc_mcontext.gregs[REG_EIP]);
#elif defined(__arm64__)
pc = (void *)(ctx->uc_mcontext.pc);
#elif defined(__arm__)
pc = (void *)(ctx->uc_mcontext.gprs[15]);
#endif
_swift_displayCrashMessage(signum, pc);
// Actually start the backtracer
run_backtracer(fd);
if (!run_backtracer(fd)) {
const char *message = "\n\nBacktracing failed\n";
if (_swift_backtraceSettings.outputTo == OutputTo::Stderr)
write(STDERR_FILENO, message, strlen(message));
else
write(STDOUT_FILENO, message, strlen(message));
}
#if !MEMSERVER_USE_PROCESS
/* If the memserver is in-process, it may have set signal handlers,
@@ -327,8 +350,8 @@ signal_for_suspend(int pid, int tid)
char pid_buffer[22];
char tid_buffer[22];
format_unsigned((unsigned)pid, pid_buffer);
format_unsigned((unsigned)tid, tid_buffer);
_swift_formatUnsigned((unsigned)pid, pid_buffer);
_swift_formatUnsigned((unsigned)tid, tid_buffer);
char status_file[6 + 22 + 6 + 22 + 7 + 1];
@@ -788,60 +811,6 @@ const char *backtracer_argv[] = {
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";
@@ -949,15 +918,15 @@ run_backtracer(int memserver_fd)
backtracer_argv[28] = trueOrFalse(_swift_backtraceSettings.cache);
format_unsigned(_swift_backtraceSettings.timeout, timeout_buf);
_swift_formatUnsigned(_swift_backtraceSettings.timeout, timeout_buf);
if (_swift_backtraceSettings.limit < 0)
std::strcpy(limit_buf, "none");
else
format_unsigned(_swift_backtraceSettings.limit, limit_buf);
_swift_formatUnsigned(_swift_backtraceSettings.limit, limit_buf);
format_unsigned(_swift_backtraceSettings.top, top_buf);
format_address(&crashInfo, addr_buf);
_swift_formatUnsigned(_swift_backtraceSettings.top, top_buf);
_swift_formatAddress(&crashInfo, addr_buf);
// Actually execute it
return _swift_spawnBacktracer(backtracer_argv, memserver_fd);

View File

@@ -44,6 +44,8 @@
#include <cstring>
#include "BacktracePrivate.h"
#ifndef lengthof
#define lengthof(x) (sizeof(x) / sizeof(x[0]))
#endif
@@ -225,9 +227,33 @@ handle_fatal_signal(int signum,
crashInfo.fault_address = (uint64_t)pinfo->si_addr;
crashInfo.mctx = (uint64_t)(((ucontext_t *)uctx)->uc_mcontext);
// Display a progress message
void *pc = 0;
ucontext_t *ctx = (ucontext_t *)uctx;
#ifdef __DARWIN_UNIX03
#define CTX_MEMBER(x) __##x
#else
#define CTX_MEMBER(x) x
#endif
#if defined(__x86_64__)
pc = (void *)(ctx->uc_mcontext->CTX_MEMBER(ss).CTX_MEMBER(rip));
#elif defined(__arm64__)
pc = (void *)(ctx->uc_mcontext->CTX_MEMBER(ss).CTX_MEMBER(pc));
#endif
_swift_displayCrashMessage(signum, pc);
/* Start the backtracer; this will suspend the process, so there's no need
to try to suspend other threads from here. */
run_backtracer();
if (!run_backtracer()) {
const char *message = "\n\nBacktracing failed\n";
if (_swift_backtraceSettings.outputTo == OutputTo::Stderr)
write(STDERR_FILENO, message, strlen(message));
else
write(STDOUT_FILENO, message, strlen(message));
}
// Restart the other threads
resume_other_threads();
@@ -275,60 +301,6 @@ const char *backtracer_argv[] = {
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";
@@ -436,15 +408,15 @@ run_backtracer()
backtracer_argv[28] = trueOrFalse(_swift_backtraceSettings.cache);
format_unsigned(_swift_backtraceSettings.timeout, timeout_buf);
_swift_formatUnsigned(_swift_backtraceSettings.timeout, timeout_buf);
if (_swift_backtraceSettings.limit < 0)
std::strcpy(limit_buf, "none");
else
format_unsigned(_swift_backtraceSettings.limit, limit_buf);
_swift_formatUnsigned(_swift_backtraceSettings.limit, limit_buf);
format_unsigned(_swift_backtraceSettings.top, top_buf);
format_address(&crashInfo, addr_buf);
_swift_formatUnsigned(_swift_backtraceSettings.top, top_buf);
_swift_formatAddress(&crashInfo, addr_buf);
// Actually execute it
return _swift_spawnBacktracer(backtracer_argv);

View File

@@ -34,7 +34,6 @@
#include "ImageInspection.h"
#include "swift/Demangling/Demangle.h"
#include "swift/Runtime/Backtrace.h"
#include "swift/Runtime/Debug.h"
#include "swift/Runtime/Portability.h"
#include "swift/Runtime/Win32.h"
@@ -71,6 +70,8 @@
#include "swift/Runtime/Atomic.h"
#endif // SWIFT_HAVE_CRASHREPORTERCLIENT
#include "BacktracePrivate.h"
namespace FatalErrorFlags {
enum: uint32_t {
ReportBacktrace = 1 << 0