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