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 41 using namespace llvm; 42 43 // Use explicit storage to avoid accessing cl::opt in a signal handler. 44 static bool DisableSymbolicationFlag = false; 45 static ManagedStatic<std::string> CrashDiagnosticsDirectory; 46 namespace { 47 struct 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 }; 55 struct 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 64 void 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 73 constexpr char DisableSymbolizationEnv[] = "LLVM_DISABLE_SYMBOLIZATION"; 74 constexpr char LLVMSymbolizerPathEnv[] = "LLVM_SYMBOLIZER_PATH"; 75 constexpr 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. 80 struct CallbackAndCookie { 81 sys::SignalHandlerCallback Callback; 82 void *Cookie; 83 enum class Status { Empty, Initializing, Initialized, Executing }; 84 std::atomic<Status> Flag; 85 }; 86 87 static 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 91 static std::array<CallbackAndCookie, MaxSignalHandlerCallbacks> & 92 CallBacksToRun() { 93 static std::array<CallbackAndCookie, MaxSignalHandlerCallbacks> callbacks; 94 return callbacks; 95 } 96 97 // Signal-safe. 98 void 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. 112 static 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 127 static 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. 134 static 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. 141 LLVM_ATTRIBUTE_USED 142 static 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 256 static bool printMarkupContext(raw_ostream &OS, const char *MainExecutableName); 257 258 LLVM_ATTRIBUTE_USED 259 static 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