10b57cec5SDimitry Andric //===- Signals.cpp - Signal Handling support --------------------*- C++ -*-===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This file defines some helpful functions for dealing with the possibility of
100b57cec5SDimitry Andric // Unix signals occurring while your program is running.
110b57cec5SDimitry Andric //
120b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
130b57cec5SDimitry Andric 
140b57cec5SDimitry Andric #include "llvm/Support/Signals.h"
15fe6060f1SDimitry Andric 
16fe6060f1SDimitry Andric #include "DebugOptions.h"
17fe6060f1SDimitry Andric 
180b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h"
190b57cec5SDimitry Andric #include "llvm/Config/llvm-config.h"
20480093f4SDimitry Andric #include "llvm/Support/CommandLine.h"
210b57cec5SDimitry Andric #include "llvm/Support/ErrorOr.h"
220b57cec5SDimitry Andric #include "llvm/Support/FileSystem.h"
230b57cec5SDimitry Andric #include "llvm/Support/FileUtilities.h"
240b57cec5SDimitry Andric #include "llvm/Support/Format.h"
25480093f4SDimitry Andric #include "llvm/Support/FormatVariadic.h"
260b57cec5SDimitry Andric #include "llvm/Support/ManagedStatic.h"
270b57cec5SDimitry Andric #include "llvm/Support/MemoryBuffer.h"
2804eeddc0SDimitry Andric #include "llvm/Support/Path.h"
290b57cec5SDimitry Andric #include "llvm/Support/Program.h"
300b57cec5SDimitry Andric #include "llvm/Support/StringSaver.h"
310b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
3281ad6265SDimitry Andric #include <array>
33bdd1243dSDimitry Andric #include <cmath>
340b57cec5SDimitry Andric #include <vector>
350b57cec5SDimitry Andric 
360b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
370b57cec5SDimitry Andric //=== WARNING: Implementation here must contain only TRULY operating system
380b57cec5SDimitry Andric //===          independent code.
390b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
400b57cec5SDimitry Andric 
410b57cec5SDimitry Andric using namespace llvm;
420b57cec5SDimitry Andric 
430b57cec5SDimitry Andric // Use explicit storage to avoid accessing cl::opt in a signal handler.
440b57cec5SDimitry Andric static bool DisableSymbolicationFlag = false;
45fe6060f1SDimitry Andric static ManagedStatic<std::string> CrashDiagnosticsDirectory;
46fe6060f1SDimitry Andric namespace {
47fe6060f1SDimitry Andric struct CreateDisableSymbolication {
call__anon97509e350111::CreateDisableSymbolication48fe6060f1SDimitry Andric   static void *call() {
49fe6060f1SDimitry Andric     return new cl::opt<bool, true>(
50fe6060f1SDimitry Andric         "disable-symbolication",
510b57cec5SDimitry Andric         cl::desc("Disable symbolizing crash backtraces."),
520b57cec5SDimitry Andric         cl::location(DisableSymbolicationFlag), cl::Hidden);
53fe6060f1SDimitry Andric   }
54fe6060f1SDimitry Andric };
55fe6060f1SDimitry Andric struct CreateCrashDiagnosticsDir {
call__anon97509e350111::CreateCrashDiagnosticsDir56fe6060f1SDimitry Andric   static void *call() {
57fe6060f1SDimitry Andric     return new cl::opt<std::string, true>(
58fe6060f1SDimitry Andric         "crash-diagnostics-dir", cl::value_desc("directory"),
59fe6060f1SDimitry Andric         cl::desc("Directory for crash diagnostic files."),
60fe6060f1SDimitry Andric         cl::location(*CrashDiagnosticsDirectory), cl::Hidden);
61fe6060f1SDimitry Andric   }
62fe6060f1SDimitry Andric };
63fe6060f1SDimitry Andric } // namespace
initSignalsOptions()64fe6060f1SDimitry Andric void llvm::initSignalsOptions() {
65fe6060f1SDimitry Andric   static ManagedStatic<cl::opt<bool, true>, CreateDisableSymbolication>
66fe6060f1SDimitry Andric       DisableSymbolication;
67fe6060f1SDimitry Andric   static ManagedStatic<cl::opt<std::string, true>, CreateCrashDiagnosticsDir>
68fe6060f1SDimitry Andric       CrashDiagnosticsDir;
69fe6060f1SDimitry Andric   *DisableSymbolication;
70fe6060f1SDimitry Andric   *CrashDiagnosticsDir;
71fe6060f1SDimitry Andric }
720b57cec5SDimitry Andric 
73e8d8bef9SDimitry Andric constexpr char DisableSymbolizationEnv[] = "LLVM_DISABLE_SYMBOLIZATION";
74e8d8bef9SDimitry Andric constexpr char LLVMSymbolizerPathEnv[] = "LLVM_SYMBOLIZER_PATH";
755f757f3fSDimitry Andric constexpr char EnableSymbolizerMarkupEnv[] = "LLVM_ENABLE_SYMBOLIZER_MARKUP";
76e8d8bef9SDimitry Andric 
770b57cec5SDimitry Andric // Callbacks to run in signal handler must be lock-free because a signal handler
780b57cec5SDimitry Andric // could be running as we add new callbacks. We don't add unbounded numbers of
790b57cec5SDimitry Andric // callbacks, an array is therefore sufficient.
800b57cec5SDimitry Andric struct CallbackAndCookie {
810b57cec5SDimitry Andric   sys::SignalHandlerCallback Callback;
820b57cec5SDimitry Andric   void *Cookie;
830b57cec5SDimitry Andric   enum class Status { Empty, Initializing, Initialized, Executing };
840b57cec5SDimitry Andric   std::atomic<Status> Flag;
850b57cec5SDimitry Andric };
8681ad6265SDimitry Andric 
870b57cec5SDimitry Andric static constexpr size_t MaxSignalHandlerCallbacks = 8;
8881ad6265SDimitry Andric 
8981ad6265SDimitry Andric // A global array of CallbackAndCookie may not compile with
9081ad6265SDimitry Andric // -Werror=global-constructors in c++20 and above
9181ad6265SDimitry Andric static std::array<CallbackAndCookie, MaxSignalHandlerCallbacks> &
CallBacksToRun()9281ad6265SDimitry Andric CallBacksToRun() {
9381ad6265SDimitry Andric   static std::array<CallbackAndCookie, MaxSignalHandlerCallbacks> callbacks;
9481ad6265SDimitry Andric   return callbacks;
9581ad6265SDimitry Andric }
960b57cec5SDimitry Andric 
970b57cec5SDimitry Andric // Signal-safe.
RunSignalHandlers()980b57cec5SDimitry Andric void sys::RunSignalHandlers() {
9981ad6265SDimitry Andric   for (CallbackAndCookie &RunMe : CallBacksToRun()) {
1000b57cec5SDimitry Andric     auto Expected = CallbackAndCookie::Status::Initialized;
1010b57cec5SDimitry Andric     auto Desired = CallbackAndCookie::Status::Executing;
1020b57cec5SDimitry Andric     if (!RunMe.Flag.compare_exchange_strong(Expected, Desired))
1030b57cec5SDimitry Andric       continue;
1040b57cec5SDimitry Andric     (*RunMe.Callback)(RunMe.Cookie);
1050b57cec5SDimitry Andric     RunMe.Callback = nullptr;
1060b57cec5SDimitry Andric     RunMe.Cookie = nullptr;
1070b57cec5SDimitry Andric     RunMe.Flag.store(CallbackAndCookie::Status::Empty);
1080b57cec5SDimitry Andric   }
1090b57cec5SDimitry Andric }
1100b57cec5SDimitry Andric 
1110b57cec5SDimitry Andric // Signal-safe.
insertSignalHandler(sys::SignalHandlerCallback FnPtr,void * Cookie)1120b57cec5SDimitry Andric static void insertSignalHandler(sys::SignalHandlerCallback FnPtr,
1130b57cec5SDimitry Andric                                 void *Cookie) {
11481ad6265SDimitry Andric   for (CallbackAndCookie &SetMe : CallBacksToRun()) {
1150b57cec5SDimitry Andric     auto Expected = CallbackAndCookie::Status::Empty;
1160b57cec5SDimitry Andric     auto Desired = CallbackAndCookie::Status::Initializing;
1170b57cec5SDimitry Andric     if (!SetMe.Flag.compare_exchange_strong(Expected, Desired))
1180b57cec5SDimitry Andric       continue;
1190b57cec5SDimitry Andric     SetMe.Callback = FnPtr;
1200b57cec5SDimitry Andric     SetMe.Cookie = Cookie;
1210b57cec5SDimitry Andric     SetMe.Flag.store(CallbackAndCookie::Status::Initialized);
1220b57cec5SDimitry Andric     return;
1230b57cec5SDimitry Andric   }
1240b57cec5SDimitry Andric   report_fatal_error("too many signal callbacks already registered");
1250b57cec5SDimitry Andric }
1260b57cec5SDimitry Andric 
1270b57cec5SDimitry Andric static bool findModulesAndOffsets(void **StackTrace, int Depth,
1280b57cec5SDimitry Andric                                   const char **Modules, intptr_t *Offsets,
1290b57cec5SDimitry Andric                                   const char *MainExecutableName,
1300b57cec5SDimitry Andric                                   StringSaver &StrPool);
1310b57cec5SDimitry Andric 
1320b57cec5SDimitry Andric /// Format a pointer value as hexadecimal. Zero pad it out so its always the
1330b57cec5SDimitry Andric /// same width.
format_ptr(void * PC)1340b57cec5SDimitry Andric static FormattedNumber format_ptr(void *PC) {
1350b57cec5SDimitry Andric   // Each byte is two hex digits plus 2 for the 0x prefix.
1360b57cec5SDimitry Andric   unsigned PtrWidth = 2 + 2 * sizeof(void *);
1370b57cec5SDimitry Andric   return format_hex((uint64_t)PC, PtrWidth);
1380b57cec5SDimitry Andric }
1390b57cec5SDimitry Andric 
1400b57cec5SDimitry Andric /// Helper that launches llvm-symbolizer and symbolizes a backtrace.
1410b57cec5SDimitry Andric LLVM_ATTRIBUTE_USED
printSymbolizedStackTrace(StringRef Argv0,void ** StackTrace,int Depth,llvm::raw_ostream & OS)1420b57cec5SDimitry Andric static bool printSymbolizedStackTrace(StringRef Argv0, void **StackTrace,
1430b57cec5SDimitry Andric                                       int Depth, llvm::raw_ostream &OS) {
144e8d8bef9SDimitry Andric   if (DisableSymbolicationFlag || getenv(DisableSymbolizationEnv))
1450b57cec5SDimitry Andric     return false;
1460b57cec5SDimitry Andric 
1470b57cec5SDimitry Andric   // Don't recursively invoke the llvm-symbolizer binary.
148cb14a3feSDimitry Andric   if (Argv0.contains("llvm-symbolizer"))
1490b57cec5SDimitry Andric     return false;
1500b57cec5SDimitry Andric 
1510b57cec5SDimitry Andric   // FIXME: Subtract necessary number from StackTrace entries to turn return addresses
1520b57cec5SDimitry Andric   // into actual instruction addresses.
1530b57cec5SDimitry Andric   // Use llvm-symbolizer tool to symbolize the stack traces. First look for it
1540b57cec5SDimitry Andric   // alongside our binary, then in $PATH.
1550b57cec5SDimitry Andric   ErrorOr<std::string> LLVMSymbolizerPathOrErr = std::error_code();
156e8d8bef9SDimitry Andric   if (const char *Path = getenv(LLVMSymbolizerPathEnv)) {
157e8d8bef9SDimitry Andric     LLVMSymbolizerPathOrErr = sys::findProgramByName(Path);
158e8d8bef9SDimitry Andric   } else if (!Argv0.empty()) {
1590b57cec5SDimitry Andric     StringRef Parent = llvm::sys::path::parent_path(Argv0);
1600b57cec5SDimitry Andric     if (!Parent.empty())
1610b57cec5SDimitry Andric       LLVMSymbolizerPathOrErr = sys::findProgramByName("llvm-symbolizer", Parent);
1620b57cec5SDimitry Andric   }
1630b57cec5SDimitry Andric   if (!LLVMSymbolizerPathOrErr)
1640b57cec5SDimitry Andric     LLVMSymbolizerPathOrErr = sys::findProgramByName("llvm-symbolizer");
1650b57cec5SDimitry Andric   if (!LLVMSymbolizerPathOrErr)
1660b57cec5SDimitry Andric     return false;
1670b57cec5SDimitry Andric   const std::string &LLVMSymbolizerPath = *LLVMSymbolizerPathOrErr;
1680b57cec5SDimitry Andric 
1690b57cec5SDimitry Andric   // If we don't know argv0 or the address of main() at this point, try
1700b57cec5SDimitry Andric   // to guess it anyway (it's possible on some platforms).
1710b57cec5SDimitry Andric   std::string MainExecutableName =
1725ffd83dbSDimitry Andric       sys::fs::exists(Argv0) ? (std::string)std::string(Argv0)
1730b57cec5SDimitry Andric                              : sys::fs::getMainExecutable(nullptr, nullptr);
1740b57cec5SDimitry Andric   BumpPtrAllocator Allocator;
1750b57cec5SDimitry Andric   StringSaver StrPool(Allocator);
1760b57cec5SDimitry Andric   std::vector<const char *> Modules(Depth, nullptr);
1770b57cec5SDimitry Andric   std::vector<intptr_t> Offsets(Depth, 0);
1780b57cec5SDimitry Andric   if (!findModulesAndOffsets(StackTrace, Depth, Modules.data(), Offsets.data(),
1790b57cec5SDimitry Andric                              MainExecutableName.c_str(), StrPool))
1800b57cec5SDimitry Andric     return false;
1810b57cec5SDimitry Andric   int InputFD;
1820b57cec5SDimitry Andric   SmallString<32> InputFile, OutputFile;
1830b57cec5SDimitry Andric   sys::fs::createTemporaryFile("symbolizer-input", "", InputFD, InputFile);
1840b57cec5SDimitry Andric   sys::fs::createTemporaryFile("symbolizer-output", "", OutputFile);
1850b57cec5SDimitry Andric   FileRemover InputRemover(InputFile.c_str());
1860b57cec5SDimitry Andric   FileRemover OutputRemover(OutputFile.c_str());
1870b57cec5SDimitry Andric 
1880b57cec5SDimitry Andric   {
1890b57cec5SDimitry Andric     raw_fd_ostream Input(InputFD, true);
1900b57cec5SDimitry Andric     for (int i = 0; i < Depth; i++) {
1910b57cec5SDimitry Andric       if (Modules[i])
1920b57cec5SDimitry Andric         Input << Modules[i] << " " << (void*)Offsets[i] << "\n";
1930b57cec5SDimitry Andric     }
1940b57cec5SDimitry Andric   }
1950b57cec5SDimitry Andric 
196bdd1243dSDimitry Andric   std::optional<StringRef> Redirects[] = {InputFile.str(), OutputFile.str(),
197fe6060f1SDimitry Andric                                           StringRef("")};
1980b57cec5SDimitry Andric   StringRef Args[] = {"llvm-symbolizer", "--functions=linkage", "--inlining",
1990b57cec5SDimitry Andric #ifdef _WIN32
2000b57cec5SDimitry Andric                       // Pass --relative-address on Windows so that we don't
2010b57cec5SDimitry Andric                       // have to add ImageBase from PE file.
2020b57cec5SDimitry Andric                       // FIXME: Make this the default for llvm-symbolizer.
2030b57cec5SDimitry Andric                       "--relative-address",
2040b57cec5SDimitry Andric #endif
2050b57cec5SDimitry Andric                       "--demangle"};
2060b57cec5SDimitry Andric   int RunResult =
207bdd1243dSDimitry Andric       sys::ExecuteAndWait(LLVMSymbolizerPath, Args, std::nullopt, Redirects);
2080b57cec5SDimitry Andric   if (RunResult != 0)
2090b57cec5SDimitry Andric     return false;
2100b57cec5SDimitry Andric 
2110b57cec5SDimitry Andric   // This report format is based on the sanitizer stack trace printer.  See
2120b57cec5SDimitry Andric   // sanitizer_stacktrace_printer.cc in compiler-rt.
2130b57cec5SDimitry Andric   auto OutputBuf = MemoryBuffer::getFile(OutputFile.c_str());
2140b57cec5SDimitry Andric   if (!OutputBuf)
2150b57cec5SDimitry Andric     return false;
2160b57cec5SDimitry Andric   StringRef Output = OutputBuf.get()->getBuffer();
2170b57cec5SDimitry Andric   SmallVector<StringRef, 32> Lines;
2180b57cec5SDimitry Andric   Output.split(Lines, "\n");
2190b57cec5SDimitry Andric   auto CurLine = Lines.begin();
2200b57cec5SDimitry Andric   int frame_no = 0;
2210b57cec5SDimitry Andric   for (int i = 0; i < Depth; i++) {
2220b57cec5SDimitry Andric     auto PrintLineHeader = [&]() {
2230b57cec5SDimitry Andric       OS << right_justify(formatv("#{0}", frame_no++).str(),
2240b57cec5SDimitry Andric                           std::log10(Depth) + 2)
2250b57cec5SDimitry Andric          << ' ' << format_ptr(StackTrace[i]) << ' ';
2260b57cec5SDimitry Andric     };
2270b57cec5SDimitry Andric     if (!Modules[i]) {
2280b57cec5SDimitry Andric       PrintLineHeader();
2290b57cec5SDimitry Andric       OS << '\n';
2300b57cec5SDimitry Andric       continue;
2310b57cec5SDimitry Andric     }
2320b57cec5SDimitry Andric     // Read pairs of lines (function name and file/line info) until we
2330b57cec5SDimitry Andric     // encounter empty line.
2340b57cec5SDimitry Andric     for (;;) {
2350b57cec5SDimitry Andric       if (CurLine == Lines.end())
2360b57cec5SDimitry Andric         return false;
2370b57cec5SDimitry Andric       StringRef FunctionName = *CurLine++;
2380b57cec5SDimitry Andric       if (FunctionName.empty())
2390b57cec5SDimitry Andric         break;
2400b57cec5SDimitry Andric       PrintLineHeader();
2415f757f3fSDimitry Andric       if (!FunctionName.starts_with("??"))
2420b57cec5SDimitry Andric         OS << FunctionName << ' ';
2430b57cec5SDimitry Andric       if (CurLine == Lines.end())
2440b57cec5SDimitry Andric         return false;
2450b57cec5SDimitry Andric       StringRef FileLineInfo = *CurLine++;
2465f757f3fSDimitry Andric       if (!FileLineInfo.starts_with("??"))
2470b57cec5SDimitry Andric         OS << FileLineInfo;
2480b57cec5SDimitry Andric       else
2490b57cec5SDimitry Andric         OS << "(" << Modules[i] << '+' << format_hex(Offsets[i], 0) << ")";
2500b57cec5SDimitry Andric       OS << "\n";
2510b57cec5SDimitry Andric     }
2520b57cec5SDimitry Andric   }
2530b57cec5SDimitry Andric   return true;
2540b57cec5SDimitry Andric }
2550b57cec5SDimitry Andric 
2565f757f3fSDimitry Andric static bool printMarkupContext(raw_ostream &OS, const char *MainExecutableName);
2575f757f3fSDimitry Andric 
2585f757f3fSDimitry Andric LLVM_ATTRIBUTE_USED
printMarkupStackTrace(StringRef Argv0,void ** StackTrace,int Depth,raw_ostream & OS)2595f757f3fSDimitry Andric static bool printMarkupStackTrace(StringRef Argv0, void **StackTrace, int Depth,
2605f757f3fSDimitry Andric                                   raw_ostream &OS) {
2615f757f3fSDimitry Andric   const char *Env = getenv(EnableSymbolizerMarkupEnv);
2625f757f3fSDimitry Andric   if (!Env || !*Env)
2635f757f3fSDimitry Andric     return false;
2645f757f3fSDimitry Andric 
2655f757f3fSDimitry Andric   std::string MainExecutableName =
2665f757f3fSDimitry Andric       sys::fs::exists(Argv0) ? std::string(Argv0)
2675f757f3fSDimitry Andric                              : sys::fs::getMainExecutable(nullptr, nullptr);
2685f757f3fSDimitry Andric   if (!printMarkupContext(OS, MainExecutableName.c_str()))
2695f757f3fSDimitry Andric     return false;
2705f757f3fSDimitry Andric   for (int I = 0; I < Depth; I++)
2715f757f3fSDimitry Andric     OS << format("{{{bt:%d:%#016x}}}\n", I, StackTrace[I]);
2725f757f3fSDimitry Andric   return true;
2735f757f3fSDimitry Andric }
2745f757f3fSDimitry Andric 
2750b57cec5SDimitry Andric // Include the platform-specific parts of this class.
2760b57cec5SDimitry Andric #ifdef LLVM_ON_UNIX
2770b57cec5SDimitry Andric #include "Unix/Signals.inc"
2780b57cec5SDimitry Andric #endif
2790b57cec5SDimitry Andric #ifdef _WIN32
2800b57cec5SDimitry Andric #include "Windows/Signals.inc"
2810b57cec5SDimitry Andric #endif
282