xref: /openbsd-src/gnu/llvm/llvm/lib/Support/Signals.cpp (revision d415bd752c734aee168c4ee86ff32e8cc249eb16)
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