109467b48Spatrick //===- Signals.cpp - Signal Handling support --------------------*- C++ -*-===//
209467b48Spatrick //
309467b48Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
409467b48Spatrick // See https://llvm.org/LICENSE.txt for license information.
509467b48Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
609467b48Spatrick //
709467b48Spatrick //===----------------------------------------------------------------------===//
809467b48Spatrick //
909467b48Spatrick // This file defines some helpful functions for dealing with the possibility of
1009467b48Spatrick // Unix signals occurring while your program is running.
1109467b48Spatrick //
1209467b48Spatrick //===----------------------------------------------------------------------===//
1309467b48Spatrick
1409467b48Spatrick #include "llvm/Support/Signals.h"
1573471bf0Spatrick
1673471bf0Spatrick #include "DebugOptions.h"
1773471bf0Spatrick
1809467b48Spatrick #include "llvm/ADT/StringRef.h"
1909467b48Spatrick #include "llvm/Config/llvm-config.h"
2009467b48Spatrick #include "llvm/Support/CommandLine.h"
2109467b48Spatrick #include "llvm/Support/ErrorOr.h"
2209467b48Spatrick #include "llvm/Support/FileSystem.h"
2309467b48Spatrick #include "llvm/Support/FileUtilities.h"
2409467b48Spatrick #include "llvm/Support/Format.h"
2509467b48Spatrick #include "llvm/Support/FormatVariadic.h"
2609467b48Spatrick #include "llvm/Support/ManagedStatic.h"
2709467b48Spatrick #include "llvm/Support/MemoryBuffer.h"
28*d415bd75Srobert #include "llvm/Support/Path.h"
2909467b48Spatrick #include "llvm/Support/Program.h"
3009467b48Spatrick #include "llvm/Support/StringSaver.h"
3109467b48Spatrick #include "llvm/Support/raw_ostream.h"
32*d415bd75Srobert #include <array>
33*d415bd75Srobert #include <cmath>
3409467b48Spatrick #include <vector>
3509467b48Spatrick
3609467b48Spatrick //===----------------------------------------------------------------------===//
3709467b48Spatrick //=== WARNING: Implementation here must contain only TRULY operating system
3809467b48Spatrick //=== independent code.
3909467b48Spatrick //===----------------------------------------------------------------------===//
4009467b48Spatrick
4109467b48Spatrick using namespace llvm;
4209467b48Spatrick
4309467b48Spatrick // Use explicit storage to avoid accessing cl::opt in a signal handler.
4409467b48Spatrick static bool DisableSymbolicationFlag = false;
4573471bf0Spatrick static ManagedStatic<std::string> CrashDiagnosticsDirectory;
4673471bf0Spatrick namespace {
4773471bf0Spatrick struct CreateDisableSymbolication {
call__anon69a20aa40111::CreateDisableSymbolication4873471bf0Spatrick static void *call() {
4973471bf0Spatrick return new cl::opt<bool, true>(
5073471bf0Spatrick "disable-symbolication",
5109467b48Spatrick cl::desc("Disable symbolizing crash backtraces."),
5209467b48Spatrick cl::location(DisableSymbolicationFlag), cl::Hidden);
5373471bf0Spatrick }
5473471bf0Spatrick };
5573471bf0Spatrick struct CreateCrashDiagnosticsDir {
call__anon69a20aa40111::CreateCrashDiagnosticsDir5673471bf0Spatrick static void *call() {
5773471bf0Spatrick return new cl::opt<std::string, true>(
5873471bf0Spatrick "crash-diagnostics-dir", cl::value_desc("directory"),
5973471bf0Spatrick cl::desc("Directory for crash diagnostic files."),
6073471bf0Spatrick cl::location(*CrashDiagnosticsDirectory), cl::Hidden);
6173471bf0Spatrick }
6273471bf0Spatrick };
6373471bf0Spatrick } // namespace
initSignalsOptions()6473471bf0Spatrick void llvm::initSignalsOptions() {
6573471bf0Spatrick static ManagedStatic<cl::opt<bool, true>, CreateDisableSymbolication>
6673471bf0Spatrick DisableSymbolication;
6773471bf0Spatrick static ManagedStatic<cl::opt<std::string, true>, CreateCrashDiagnosticsDir>
6873471bf0Spatrick CrashDiagnosticsDir;
6973471bf0Spatrick *DisableSymbolication;
7073471bf0Spatrick *CrashDiagnosticsDir;
7173471bf0Spatrick }
7273471bf0Spatrick
7373471bf0Spatrick constexpr char DisableSymbolizationEnv[] = "LLVM_DISABLE_SYMBOLIZATION";
7473471bf0Spatrick constexpr char LLVMSymbolizerPathEnv[] = "LLVM_SYMBOLIZER_PATH";
7509467b48Spatrick
7609467b48Spatrick // Callbacks to run in signal handler must be lock-free because a signal handler
7709467b48Spatrick // could be running as we add new callbacks. We don't add unbounded numbers of
7809467b48Spatrick // callbacks, an array is therefore sufficient.
7909467b48Spatrick struct CallbackAndCookie {
8009467b48Spatrick sys::SignalHandlerCallback Callback;
8109467b48Spatrick void *Cookie;
8209467b48Spatrick enum class Status { Empty, Initializing, Initialized, Executing };
8309467b48Spatrick std::atomic<Status> Flag;
8409467b48Spatrick };
85*d415bd75Srobert
8609467b48Spatrick static constexpr size_t MaxSignalHandlerCallbacks = 8;
87*d415bd75Srobert
88*d415bd75Srobert // A global array of CallbackAndCookie may not compile with
89*d415bd75Srobert // -Werror=global-constructors in c++20 and above
90*d415bd75Srobert static std::array<CallbackAndCookie, MaxSignalHandlerCallbacks> &
CallBacksToRun()91*d415bd75Srobert CallBacksToRun() {
92*d415bd75Srobert static std::array<CallbackAndCookie, MaxSignalHandlerCallbacks> callbacks;
93*d415bd75Srobert return callbacks;
94*d415bd75Srobert }
9509467b48Spatrick
9609467b48Spatrick // Signal-safe.
RunSignalHandlers()9709467b48Spatrick void sys::RunSignalHandlers() {
98*d415bd75Srobert for (CallbackAndCookie &RunMe : CallBacksToRun()) {
9909467b48Spatrick auto Expected = CallbackAndCookie::Status::Initialized;
10009467b48Spatrick auto Desired = CallbackAndCookie::Status::Executing;
10109467b48Spatrick if (!RunMe.Flag.compare_exchange_strong(Expected, Desired))
10209467b48Spatrick continue;
10309467b48Spatrick (*RunMe.Callback)(RunMe.Cookie);
10409467b48Spatrick RunMe.Callback = nullptr;
10509467b48Spatrick RunMe.Cookie = nullptr;
10609467b48Spatrick RunMe.Flag.store(CallbackAndCookie::Status::Empty);
10709467b48Spatrick }
10809467b48Spatrick }
10909467b48Spatrick
11009467b48Spatrick // Signal-safe.
insertSignalHandler(sys::SignalHandlerCallback FnPtr,void * Cookie)11109467b48Spatrick static void insertSignalHandler(sys::SignalHandlerCallback FnPtr,
11209467b48Spatrick void *Cookie) {
113*d415bd75Srobert for (CallbackAndCookie &SetMe : CallBacksToRun()) {
11409467b48Spatrick auto Expected = CallbackAndCookie::Status::Empty;
11509467b48Spatrick auto Desired = CallbackAndCookie::Status::Initializing;
11609467b48Spatrick if (!SetMe.Flag.compare_exchange_strong(Expected, Desired))
11709467b48Spatrick continue;
11809467b48Spatrick SetMe.Callback = FnPtr;
11909467b48Spatrick SetMe.Cookie = Cookie;
12009467b48Spatrick SetMe.Flag.store(CallbackAndCookie::Status::Initialized);
12109467b48Spatrick return;
12209467b48Spatrick }
12309467b48Spatrick report_fatal_error("too many signal callbacks already registered");
12409467b48Spatrick }
12509467b48Spatrick
12609467b48Spatrick static bool findModulesAndOffsets(void **StackTrace, int Depth,
12709467b48Spatrick const char **Modules, intptr_t *Offsets,
12809467b48Spatrick const char *MainExecutableName,
12909467b48Spatrick StringSaver &StrPool);
13009467b48Spatrick
13109467b48Spatrick /// Format a pointer value as hexadecimal. Zero pad it out so its always the
13209467b48Spatrick /// same width.
format_ptr(void * PC)13309467b48Spatrick static FormattedNumber format_ptr(void *PC) {
13409467b48Spatrick // Each byte is two hex digits plus 2 for the 0x prefix.
13509467b48Spatrick unsigned PtrWidth = 2 + 2 * sizeof(void *);
13609467b48Spatrick return format_hex((uint64_t)PC, PtrWidth);
13709467b48Spatrick }
13809467b48Spatrick
13909467b48Spatrick /// Helper that launches llvm-symbolizer and symbolizes a backtrace.
14009467b48Spatrick LLVM_ATTRIBUTE_USED
printSymbolizedStackTrace(StringRef Argv0,void ** StackTrace,int Depth,llvm::raw_ostream & OS)14109467b48Spatrick static bool printSymbolizedStackTrace(StringRef Argv0, void **StackTrace,
14209467b48Spatrick int Depth, llvm::raw_ostream &OS) {
14373471bf0Spatrick if (DisableSymbolicationFlag || getenv(DisableSymbolizationEnv))
14409467b48Spatrick return false;
14509467b48Spatrick
14609467b48Spatrick // Don't recursively invoke the llvm-symbolizer binary.
14709467b48Spatrick if (Argv0.find("llvm-symbolizer") != std::string::npos)
14809467b48Spatrick return false;
14909467b48Spatrick
15009467b48Spatrick // FIXME: Subtract necessary number from StackTrace entries to turn return addresses
15109467b48Spatrick // into actual instruction addresses.
15209467b48Spatrick // Use llvm-symbolizer tool to symbolize the stack traces. First look for it
15309467b48Spatrick // alongside our binary, then in $PATH.
15409467b48Spatrick ErrorOr<std::string> LLVMSymbolizerPathOrErr = std::error_code();
15573471bf0Spatrick if (const char *Path = getenv(LLVMSymbolizerPathEnv)) {
15673471bf0Spatrick LLVMSymbolizerPathOrErr = sys::findProgramByName(Path);
15773471bf0Spatrick } else if (!Argv0.empty()) {
15809467b48Spatrick StringRef Parent = llvm::sys::path::parent_path(Argv0);
15909467b48Spatrick if (!Parent.empty())
16009467b48Spatrick LLVMSymbolizerPathOrErr = sys::findProgramByName("llvm-symbolizer", Parent);
16109467b48Spatrick }
16209467b48Spatrick if (!LLVMSymbolizerPathOrErr)
16309467b48Spatrick LLVMSymbolizerPathOrErr = sys::findProgramByName("llvm-symbolizer");
16409467b48Spatrick if (!LLVMSymbolizerPathOrErr)
16509467b48Spatrick return false;
16609467b48Spatrick const std::string &LLVMSymbolizerPath = *LLVMSymbolizerPathOrErr;
16709467b48Spatrick
16809467b48Spatrick // If we don't know argv0 or the address of main() at this point, try
16909467b48Spatrick // to guess it anyway (it's possible on some platforms).
17009467b48Spatrick std::string MainExecutableName =
171097a140dSpatrick sys::fs::exists(Argv0) ? (std::string)std::string(Argv0)
17209467b48Spatrick : sys::fs::getMainExecutable(nullptr, nullptr);
17309467b48Spatrick BumpPtrAllocator Allocator;
17409467b48Spatrick StringSaver StrPool(Allocator);
17509467b48Spatrick std::vector<const char *> Modules(Depth, nullptr);
17609467b48Spatrick std::vector<intptr_t> Offsets(Depth, 0);
17709467b48Spatrick if (!findModulesAndOffsets(StackTrace, Depth, Modules.data(), Offsets.data(),
17809467b48Spatrick MainExecutableName.c_str(), StrPool))
17909467b48Spatrick return false;
18009467b48Spatrick int InputFD;
18109467b48Spatrick SmallString<32> InputFile, OutputFile;
18209467b48Spatrick sys::fs::createTemporaryFile("symbolizer-input", "", InputFD, InputFile);
18309467b48Spatrick sys::fs::createTemporaryFile("symbolizer-output", "", OutputFile);
18409467b48Spatrick FileRemover InputRemover(InputFile.c_str());
18509467b48Spatrick FileRemover OutputRemover(OutputFile.c_str());
18609467b48Spatrick
18709467b48Spatrick {
18809467b48Spatrick raw_fd_ostream Input(InputFD, true);
18909467b48Spatrick for (int i = 0; i < Depth; i++) {
19009467b48Spatrick if (Modules[i])
19109467b48Spatrick Input << Modules[i] << " " << (void*)Offsets[i] << "\n";
19209467b48Spatrick }
19309467b48Spatrick }
19409467b48Spatrick
195*d415bd75Srobert std::optional<StringRef> Redirects[] = {InputFile.str(), OutputFile.str(),
19673471bf0Spatrick StringRef("")};
19709467b48Spatrick StringRef Args[] = {"llvm-symbolizer", "--functions=linkage", "--inlining",
19809467b48Spatrick #ifdef _WIN32
19909467b48Spatrick // Pass --relative-address on Windows so that we don't
20009467b48Spatrick // have to add ImageBase from PE file.
20109467b48Spatrick // FIXME: Make this the default for llvm-symbolizer.
20209467b48Spatrick "--relative-address",
20309467b48Spatrick #endif
20409467b48Spatrick "--demangle"};
20509467b48Spatrick int RunResult =
206*d415bd75Srobert sys::ExecuteAndWait(LLVMSymbolizerPath, Args, std::nullopt, Redirects);
20709467b48Spatrick if (RunResult != 0)
20809467b48Spatrick return false;
20909467b48Spatrick
21009467b48Spatrick // This report format is based on the sanitizer stack trace printer. See
21109467b48Spatrick // sanitizer_stacktrace_printer.cc in compiler-rt.
21209467b48Spatrick auto OutputBuf = MemoryBuffer::getFile(OutputFile.c_str());
21309467b48Spatrick if (!OutputBuf)
21409467b48Spatrick return false;
21509467b48Spatrick StringRef Output = OutputBuf.get()->getBuffer();
21609467b48Spatrick SmallVector<StringRef, 32> Lines;
21709467b48Spatrick Output.split(Lines, "\n");
21809467b48Spatrick auto CurLine = Lines.begin();
21909467b48Spatrick int frame_no = 0;
22009467b48Spatrick for (int i = 0; i < Depth; i++) {
22109467b48Spatrick auto PrintLineHeader = [&]() {
22209467b48Spatrick OS << right_justify(formatv("#{0}", frame_no++).str(),
22309467b48Spatrick std::log10(Depth) + 2)
22409467b48Spatrick << ' ' << format_ptr(StackTrace[i]) << ' ';
22509467b48Spatrick };
22609467b48Spatrick if (!Modules[i]) {
22709467b48Spatrick PrintLineHeader();
22809467b48Spatrick OS << '\n';
22909467b48Spatrick continue;
23009467b48Spatrick }
23109467b48Spatrick // Read pairs of lines (function name and file/line info) until we
23209467b48Spatrick // encounter empty line.
23309467b48Spatrick for (;;) {
23409467b48Spatrick if (CurLine == Lines.end())
23509467b48Spatrick return false;
23609467b48Spatrick StringRef FunctionName = *CurLine++;
23709467b48Spatrick if (FunctionName.empty())
23809467b48Spatrick break;
23909467b48Spatrick PrintLineHeader();
24009467b48Spatrick if (!FunctionName.startswith("??"))
24109467b48Spatrick OS << FunctionName << ' ';
24209467b48Spatrick if (CurLine == Lines.end())
24309467b48Spatrick return false;
24409467b48Spatrick StringRef FileLineInfo = *CurLine++;
24509467b48Spatrick if (!FileLineInfo.startswith("??"))
24609467b48Spatrick OS << FileLineInfo;
24709467b48Spatrick else
24809467b48Spatrick OS << "(" << Modules[i] << '+' << format_hex(Offsets[i], 0) << ")";
24909467b48Spatrick OS << "\n";
25009467b48Spatrick }
25109467b48Spatrick }
25209467b48Spatrick return true;
25309467b48Spatrick }
25409467b48Spatrick
25509467b48Spatrick // Include the platform-specific parts of this class.
25609467b48Spatrick #ifdef LLVM_ON_UNIX
25709467b48Spatrick #include "Unix/Signals.inc"
25809467b48Spatrick #endif
25909467b48Spatrick #ifdef _WIN32
26009467b48Spatrick #include "Windows/Signals.inc"
26109467b48Spatrick #endif
262