1//===- Signals.cpp - Signal Handling support --------------------*- C++ -*-===// 2// 3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4// See https://llvm.org/LICENSE.txt for license information. 5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6// 7//===----------------------------------------------------------------------===// 8// 9// This file defines some helpful functions for dealing with the possibility of 10// Unix signals occurring while your program is running. 11// 12//===----------------------------------------------------------------------===// 13 14#include "llvm/Support/Signals.h" 15 16#include "DebugOptions.h" 17 18#include "llvm/ADT/StringRef.h" 19#include "llvm/Config/llvm-config.h" 20#include "llvm/Support/CommandLine.h" 21#include "llvm/Support/ErrorOr.h" 22#include "llvm/Support/FileSystem.h" 23#include "llvm/Support/FileUtilities.h" 24#include "llvm/Support/Format.h" 25#include "llvm/Support/FormatVariadic.h" 26#include "llvm/Support/ManagedStatic.h" 27#include "llvm/Support/MemoryBuffer.h" 28#include "llvm/Support/Path.h" 29#include "llvm/Support/Program.h" 30#include "llvm/Support/StringSaver.h" 31#include "llvm/Support/raw_ostream.h" 32#include <array> 33#include <cmath> 34#include <vector> 35 36//===----------------------------------------------------------------------===// 37//=== WARNING: Implementation here must contain only TRULY operating system 38//=== independent code. 39//===----------------------------------------------------------------------===// 40 41using namespace llvm; 42 43// Use explicit storage to avoid accessing cl::opt in a signal handler. 44static bool DisableSymbolicationFlag = false; 45static ManagedStatic<std::string> CrashDiagnosticsDirectory; 46namespace { 47struct CreateDisableSymbolication { 48 static void *call() { 49 return new cl::opt<bool, true>( 50 "disable-symbolication", 51 cl::desc("Disable symbolizing crash backtraces."), 52 cl::location(DisableSymbolicationFlag), cl::Hidden); 53 } 54}; 55struct CreateCrashDiagnosticsDir { 56 static void *call() { 57 return new cl::opt<std::string, true>( 58 "crash-diagnostics-dir", cl::value_desc("directory"), 59 cl::desc("Directory for crash diagnostic files."), 60 cl::location(*CrashDiagnosticsDirectory), cl::Hidden); 61 } 62}; 63} // namespace 64void llvm::initSignalsOptions() { 65 static ManagedStatic<cl::opt<bool, true>, CreateDisableSymbolication> 66 DisableSymbolication; 67 static ManagedStatic<cl::opt<std::string, true>, CreateCrashDiagnosticsDir> 68 CrashDiagnosticsDir; 69 *DisableSymbolication; 70 *CrashDiagnosticsDir; 71} 72 73constexpr char DisableSymbolizationEnv[] = "LLVM_DISABLE_SYMBOLIZATION"; 74constexpr char LLVMSymbolizerPathEnv[] = "LLVM_SYMBOLIZER_PATH"; 75constexpr char EnableSymbolizerMarkupEnv[] = "LLVM_ENABLE_SYMBOLIZER_MARKUP"; 76 77// Callbacks to run in signal handler must be lock-free because a signal handler 78// could be running as we add new callbacks. We don't add unbounded numbers of 79// callbacks, an array is therefore sufficient. 80struct CallbackAndCookie { 81 sys::SignalHandlerCallback Callback; 82 void *Cookie; 83 enum class Status { Empty, Initializing, Initialized, Executing }; 84 std::atomic<Status> Flag; 85}; 86 87static constexpr size_t MaxSignalHandlerCallbacks = 8; 88 89// A global array of CallbackAndCookie may not compile with 90// -Werror=global-constructors in c++20 and above 91static std::array<CallbackAndCookie, MaxSignalHandlerCallbacks> & 92CallBacksToRun() { 93 static std::array<CallbackAndCookie, MaxSignalHandlerCallbacks> callbacks; 94 return callbacks; 95} 96 97// Signal-safe. 98void sys::RunSignalHandlers() { 99 for (CallbackAndCookie &RunMe : CallBacksToRun()) { 100 auto Expected = CallbackAndCookie::Status::Initialized; 101 auto Desired = CallbackAndCookie::Status::Executing; 102 if (!RunMe.Flag.compare_exchange_strong(Expected, Desired)) 103 continue; 104 (*RunMe.Callback)(RunMe.Cookie); 105 RunMe.Callback = nullptr; 106 RunMe.Cookie = nullptr; 107 RunMe.Flag.store(CallbackAndCookie::Status::Empty); 108 } 109} 110 111// Signal-safe. 112static void insertSignalHandler(sys::SignalHandlerCallback FnPtr, 113 void *Cookie) { 114 for (CallbackAndCookie &SetMe : CallBacksToRun()) { 115 auto Expected = CallbackAndCookie::Status::Empty; 116 auto Desired = CallbackAndCookie::Status::Initializing; 117 if (!SetMe.Flag.compare_exchange_strong(Expected, Desired)) 118 continue; 119 SetMe.Callback = FnPtr; 120 SetMe.Cookie = Cookie; 121 SetMe.Flag.store(CallbackAndCookie::Status::Initialized); 122 return; 123 } 124 report_fatal_error("too many signal callbacks already registered"); 125} 126 127static bool findModulesAndOffsets(void **StackTrace, int Depth, 128 const char **Modules, intptr_t *Offsets, 129 const char *MainExecutableName, 130 StringSaver &StrPool); 131 132/// Format a pointer value as hexadecimal. Zero pad it out so its always the 133/// same width. 134static FormattedNumber format_ptr(void *PC) { 135 // Each byte is two hex digits plus 2 for the 0x prefix. 136 unsigned PtrWidth = 2 + 2 * sizeof(void *); 137 return format_hex((uint64_t)PC, PtrWidth); 138} 139 140/// Helper that launches llvm-symbolizer and symbolizes a backtrace. 141LLVM_ATTRIBUTE_USED 142static bool printSymbolizedStackTrace(StringRef Argv0, void **StackTrace, 143 int Depth, llvm::raw_ostream &OS) { 144 if (DisableSymbolicationFlag || getenv(DisableSymbolizationEnv)) 145 return false; 146 147 // Don't recursively invoke the llvm-symbolizer binary. 148 if (Argv0.contains("llvm-symbolizer")) 149 return false; 150 151 // FIXME: Subtract necessary number from StackTrace entries to turn return addresses 152 // into actual instruction addresses. 153 // Use llvm-symbolizer tool to symbolize the stack traces. First look for it 154 // alongside our binary, then in $PATH. 155 ErrorOr<std::string> LLVMSymbolizerPathOrErr = std::error_code(); 156 if (const char *Path = getenv(LLVMSymbolizerPathEnv)) { 157 LLVMSymbolizerPathOrErr = sys::findProgramByName(Path); 158 } else if (!Argv0.empty()) { 159 StringRef Parent = llvm::sys::path::parent_path(Argv0); 160 if (!Parent.empty()) 161 LLVMSymbolizerPathOrErr = sys::findProgramByName("llvm-symbolizer", Parent); 162 } 163 if (!LLVMSymbolizerPathOrErr) 164 LLVMSymbolizerPathOrErr = sys::findProgramByName("llvm-symbolizer"); 165 if (!LLVMSymbolizerPathOrErr) 166 return false; 167 const std::string &LLVMSymbolizerPath = *LLVMSymbolizerPathOrErr; 168 169 // If we don't know argv0 or the address of main() at this point, try 170 // to guess it anyway (it's possible on some platforms). 171 std::string MainExecutableName = 172 sys::fs::exists(Argv0) ? (std::string)std::string(Argv0) 173 : sys::fs::getMainExecutable(nullptr, nullptr); 174 BumpPtrAllocator Allocator; 175 StringSaver StrPool(Allocator); 176 std::vector<const char *> Modules(Depth, nullptr); 177 std::vector<intptr_t> Offsets(Depth, 0); 178 if (!findModulesAndOffsets(StackTrace, Depth, Modules.data(), Offsets.data(), 179 MainExecutableName.c_str(), StrPool)) 180 return false; 181 int InputFD; 182 SmallString<32> InputFile, OutputFile; 183 sys::fs::createTemporaryFile("symbolizer-input", "", InputFD, InputFile); 184 sys::fs::createTemporaryFile("symbolizer-output", "", OutputFile); 185 FileRemover InputRemover(InputFile.c_str()); 186 FileRemover OutputRemover(OutputFile.c_str()); 187 188 { 189 raw_fd_ostream Input(InputFD, true); 190 for (int i = 0; i < Depth; i++) { 191 if (Modules[i]) 192 Input << Modules[i] << " " << (void*)Offsets[i] << "\n"; 193 } 194 } 195 196 std::optional<StringRef> Redirects[] = {InputFile.str(), OutputFile.str(), 197 StringRef("")}; 198 StringRef Args[] = {"llvm-symbolizer", "--functions=linkage", "--inlining", 199#ifdef _WIN32 200 // Pass --relative-address on Windows so that we don't 201 // have to add ImageBase from PE file. 202 // FIXME: Make this the default for llvm-symbolizer. 203 "--relative-address", 204#endif 205 "--demangle"}; 206 int RunResult = 207 sys::ExecuteAndWait(LLVMSymbolizerPath, Args, std::nullopt, Redirects); 208 if (RunResult != 0) 209 return false; 210 211 // This report format is based on the sanitizer stack trace printer. See 212 // sanitizer_stacktrace_printer.cc in compiler-rt. 213 auto OutputBuf = MemoryBuffer::getFile(OutputFile.c_str()); 214 if (!OutputBuf) 215 return false; 216 StringRef Output = OutputBuf.get()->getBuffer(); 217 SmallVector<StringRef, 32> Lines; 218 Output.split(Lines, "\n"); 219 auto CurLine = Lines.begin(); 220 int frame_no = 0; 221 for (int i = 0; i < Depth; i++) { 222 auto PrintLineHeader = [&]() { 223 OS << right_justify(formatv("#{0}", frame_no++).str(), 224 std::log10(Depth) + 2) 225 << ' ' << format_ptr(StackTrace[i]) << ' '; 226 }; 227 if (!Modules[i]) { 228 PrintLineHeader(); 229 OS << '\n'; 230 continue; 231 } 232 // Read pairs of lines (function name and file/line info) until we 233 // encounter empty line. 234 for (;;) { 235 if (CurLine == Lines.end()) 236 return false; 237 StringRef FunctionName = *CurLine++; 238 if (FunctionName.empty()) 239 break; 240 PrintLineHeader(); 241 if (!FunctionName.starts_with("??")) 242 OS << FunctionName << ' '; 243 if (CurLine == Lines.end()) 244 return false; 245 StringRef FileLineInfo = *CurLine++; 246 if (!FileLineInfo.starts_with("??")) 247 OS << FileLineInfo; 248 else 249 OS << "(" << Modules[i] << '+' << format_hex(Offsets[i], 0) << ")"; 250 OS << "\n"; 251 } 252 } 253 return true; 254} 255 256static bool printMarkupContext(raw_ostream &OS, const char *MainExecutableName); 257 258LLVM_ATTRIBUTE_USED 259static bool printMarkupStackTrace(StringRef Argv0, void **StackTrace, int Depth, 260 raw_ostream &OS) { 261 const char *Env = getenv(EnableSymbolizerMarkupEnv); 262 if (!Env || !*Env) 263 return false; 264 265 std::string MainExecutableName = 266 sys::fs::exists(Argv0) ? std::string(Argv0) 267 : sys::fs::getMainExecutable(nullptr, nullptr); 268 if (!printMarkupContext(OS, MainExecutableName.c_str())) 269 return false; 270 for (int I = 0; I < Depth; I++) 271 OS << format("{{{bt:%d:%#016x}}}\n", I, StackTrace[I]); 272 return true; 273} 274 275// Include the platform-specific parts of this class. 276#ifdef LLVM_ON_UNIX 277#include "Unix/Signals.inc" 278#endif 279#ifdef _WIN32 280#include "Windows/Signals.inc" 281#endif 282