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 { 48fe6060f1SDimitry 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 { 56fe6060f1SDimitry 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 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> & 9281ad6265SDimitry Andric CallBacksToRun() { 9381ad6265SDimitry Andric static std::array<CallbackAndCookie, MaxSignalHandlerCallbacks> callbacks; 9481ad6265SDimitry Andric return callbacks; 9581ad6265SDimitry Andric } 960b57cec5SDimitry Andric 970b57cec5SDimitry Andric // Signal-safe. 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. 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. 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 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. 148*cb14a3feSDimitry 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 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