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