Reid Spencer | 3d7a614 | 2004-08-29 19:22:48 +0000 | [diff] [blame] | 1 | //===- Signals.cpp - Signal Handling support --------------------*- C++ -*-===// |
Misha Brukman | 10468d8 | 2005-04-21 22:55:34 +0000 | [diff] [blame] | 2 | // |
Chandler Carruth | 2946cd7 | 2019-01-19 08:50:56 +0000 | [diff] [blame] | 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 |
Misha Brukman | 10468d8 | 2005-04-21 22:55:34 +0000 | [diff] [blame] | 6 | // |
Reid Spencer | 3d7a614 | 2004-08-29 19:22:48 +0000 | [diff] [blame] | 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // This file defines some helpful functions for dealing with the possibility of |
Chris Lattner | 0ab5e2c | 2011-04-15 05:18:47 +0000 | [diff] [blame] | 10 | // Unix signals occurring while your program is running. |
Reid Spencer | 3d7a614 | 2004-08-29 19:22:48 +0000 | [diff] [blame] | 11 | // |
| 12 | //===----------------------------------------------------------------------===// |
| 13 | |
Chandler Carruth | 6bda14b | 2017-06-06 11:49:48 +0000 | [diff] [blame] | 14 | #include "llvm/Support/Signals.h" |
Reid Kleckner | ba5757d | 2015-11-05 01:07:54 +0000 | [diff] [blame] | 15 | #include "llvm/ADT/STLExtras.h" |
| 16 | #include "llvm/ADT/StringRef.h" |
Nico Weber | 432a388 | 2018-04-30 14:59:11 +0000 | [diff] [blame] | 17 | #include "llvm/Config/llvm-config.h" |
Reid Kleckner | ba5757d | 2015-11-05 01:07:54 +0000 | [diff] [blame] | 18 | #include "llvm/Support/ErrorOr.h" |
| 19 | #include "llvm/Support/FileSystem.h" |
| 20 | #include "llvm/Support/FileUtilities.h" |
| 21 | #include "llvm/Support/Format.h" |
Alexandre Ganea | b67d91e | 2018-12-18 18:13:13 +0000 | [diff] [blame] | 22 | #include "llvm/Support/FormatVariadic.h" |
| 23 | #include "llvm/Support/FormatAdapters.h" |
Yaron Keren | 240bd9c | 2015-07-22 19:01:14 +0000 | [diff] [blame] | 24 | #include "llvm/Support/ManagedStatic.h" |
Reid Kleckner | ba5757d | 2015-11-05 01:07:54 +0000 | [diff] [blame] | 25 | #include "llvm/Support/MemoryBuffer.h" |
| 26 | #include "llvm/Support/Mutex.h" |
| 27 | #include "llvm/Support/Program.h" |
Reid Kleckner | ba5757d | 2015-11-05 01:07:54 +0000 | [diff] [blame] | 28 | #include "llvm/Support/StringSaver.h" |
| 29 | #include "llvm/Support/raw_ostream.h" |
David Blaikie | 4a60d37 | 2017-06-09 07:29:03 +0000 | [diff] [blame] | 30 | #include "llvm/Support/Options.h" |
Yaron Keren | 240bd9c | 2015-07-22 19:01:14 +0000 | [diff] [blame] | 31 | #include <vector> |
Reid Spencer | 3d7a614 | 2004-08-29 19:22:48 +0000 | [diff] [blame] | 32 | |
Reid Spencer | 3d7a614 | 2004-08-29 19:22:48 +0000 | [diff] [blame] | 33 | //===----------------------------------------------------------------------===// |
| 34 | //=== WARNING: Implementation here must contain only TRULY operating system |
Misha Brukman | 10468d8 | 2005-04-21 22:55:34 +0000 | [diff] [blame] | 35 | //=== independent code. |
Reid Spencer | 3d7a614 | 2004-08-29 19:22:48 +0000 | [diff] [blame] | 36 | //===----------------------------------------------------------------------===// |
| 37 | |
David Blaikie | 4a60d37 | 2017-06-09 07:29:03 +0000 | [diff] [blame] | 38 | using namespace llvm; |
| 39 | |
JF Bastien | aa1333a | 2018-05-16 17:25:35 +0000 | [diff] [blame] | 40 | // Use explicit storage to avoid accessing cl::opt in a signal handler. |
| 41 | static bool DisableSymbolicationFlag = false; |
| 42 | static cl::opt<bool, true> |
David Blaikie | 4a60d37 | 2017-06-09 07:29:03 +0000 | [diff] [blame] | 43 | DisableSymbolication("disable-symbolication", |
| 44 | cl::desc("Disable symbolizing crash backtraces."), |
JF Bastien | aa1333a | 2018-05-16 17:25:35 +0000 | [diff] [blame] | 45 | cl::location(DisableSymbolicationFlag), cl::Hidden); |
David Blaikie | 4a60d37 | 2017-06-09 07:29:03 +0000 | [diff] [blame] | 46 | |
JF Bastien | aa1333a | 2018-05-16 17:25:35 +0000 | [diff] [blame] | 47 | // Callbacks to run in signal handler must be lock-free because a signal handler |
| 48 | // could be running as we add new callbacks. We don't add unbounded numbers of |
| 49 | // callbacks, an array is therefore sufficient. |
| 50 | struct CallbackAndCookie { |
| 51 | sys::SignalHandlerCallback Callback; |
| 52 | void *Cookie; |
| 53 | enum class Status { Empty, Initializing, Initialized, Executing }; |
| 54 | std::atomic<Status> Flag; |
| 55 | }; |
| 56 | static constexpr size_t MaxSignalHandlerCallbacks = 8; |
| 57 | static CallbackAndCookie CallBacksToRun[MaxSignalHandlerCallbacks]; |
| 58 | |
| 59 | // Signal-safe. |
JF Bastien | b8931c1 | 2018-05-16 04:36:37 +0000 | [diff] [blame] | 60 | void sys::RunSignalHandlers() { |
JF Bastien | aa1333a | 2018-05-16 17:25:35 +0000 | [diff] [blame] | 61 | for (size_t I = 0; I < MaxSignalHandlerCallbacks; ++I) { |
| 62 | auto &RunMe = CallBacksToRun[I]; |
| 63 | auto Expected = CallbackAndCookie::Status::Initialized; |
| 64 | auto Desired = CallbackAndCookie::Status::Executing; |
| 65 | if (!RunMe.Flag.compare_exchange_strong(Expected, Desired)) |
| 66 | continue; |
| 67 | (*RunMe.Callback)(RunMe.Cookie); |
| 68 | RunMe.Callback = nullptr; |
| 69 | RunMe.Cookie = nullptr; |
| 70 | RunMe.Flag.store(CallbackAndCookie::Status::Empty); |
| 71 | } |
| 72 | } |
| 73 | |
| 74 | // Signal-safe. |
| 75 | static void insertSignalHandler(sys::SignalHandlerCallback FnPtr, |
| 76 | void *Cookie) { |
| 77 | for (size_t I = 0; I < MaxSignalHandlerCallbacks; ++I) { |
| 78 | auto &SetMe = CallBacksToRun[I]; |
| 79 | auto Expected = CallbackAndCookie::Status::Empty; |
| 80 | auto Desired = CallbackAndCookie::Status::Initializing; |
| 81 | if (!SetMe.Flag.compare_exchange_strong(Expected, Desired)) |
| 82 | continue; |
| 83 | SetMe.Callback = FnPtr; |
| 84 | SetMe.Cookie = Cookie; |
| 85 | SetMe.Flag.store(CallbackAndCookie::Status::Initialized); |
JF Bastien | b8931c1 | 2018-05-16 04:36:37 +0000 | [diff] [blame] | 86 | return; |
JF Bastien | aa1333a | 2018-05-16 17:25:35 +0000 | [diff] [blame] | 87 | } |
| 88 | report_fatal_error("too many signal callbacks already registered"); |
Yaron Keren | 240bd9c | 2015-07-22 19:01:14 +0000 | [diff] [blame] | 89 | } |
Reid Kleckner | ba5757d | 2015-11-05 01:07:54 +0000 | [diff] [blame] | 90 | |
| 91 | static bool findModulesAndOffsets(void **StackTrace, int Depth, |
| 92 | const char **Modules, intptr_t *Offsets, |
| 93 | const char *MainExecutableName, |
| 94 | StringSaver &StrPool); |
| 95 | |
| 96 | /// Format a pointer value as hexadecimal. Zero pad it out so its always the |
| 97 | /// same width. |
| 98 | static FormattedNumber format_ptr(void *PC) { |
| 99 | // Each byte is two hex digits plus 2 for the 0x prefix. |
| 100 | unsigned PtrWidth = 2 + 2 * sizeof(void *); |
| 101 | return format_hex((uint64_t)PC, PtrWidth); |
| 102 | } |
| 103 | |
| 104 | /// Helper that launches llvm-symbolizer and symbolizes a backtrace. |
Fangrui Song | 862eebb | 2018-05-05 20:14:38 +0000 | [diff] [blame] | 105 | LLVM_ATTRIBUTE_USED |
| 106 | static bool printSymbolizedStackTrace(StringRef Argv0, void **StackTrace, |
| 107 | int Depth, llvm::raw_ostream &OS) { |
JF Bastien | aa1333a | 2018-05-16 17:25:35 +0000 | [diff] [blame] | 108 | if (DisableSymbolicationFlag) |
David Blaikie | 4a60d37 | 2017-06-09 07:29:03 +0000 | [diff] [blame] | 109 | return false; |
| 110 | |
Richard Smith | 2ad6d48 | 2016-06-09 00:53:21 +0000 | [diff] [blame] | 111 | // Don't recursively invoke the llvm-symbolizer binary. |
| 112 | if (Argv0.find("llvm-symbolizer") != std::string::npos) |
| 113 | return false; |
| 114 | |
Reid Kleckner | ba5757d | 2015-11-05 01:07:54 +0000 | [diff] [blame] | 115 | // FIXME: Subtract necessary number from StackTrace entries to turn return addresses |
| 116 | // into actual instruction addresses. |
Richard Smith | 2ad6d48 | 2016-06-09 00:53:21 +0000 | [diff] [blame] | 117 | // Use llvm-symbolizer tool to symbolize the stack traces. First look for it |
| 118 | // alongside our binary, then in $PATH. |
| 119 | ErrorOr<std::string> LLVMSymbolizerPathOrErr = std::error_code(); |
| 120 | if (!Argv0.empty()) { |
| 121 | StringRef Parent = llvm::sys::path::parent_path(Argv0); |
| 122 | if (!Parent.empty()) |
| 123 | LLVMSymbolizerPathOrErr = sys::findProgramByName("llvm-symbolizer", Parent); |
| 124 | } |
| 125 | if (!LLVMSymbolizerPathOrErr) |
| 126 | LLVMSymbolizerPathOrErr = sys::findProgramByName("llvm-symbolizer"); |
Reid Kleckner | ba5757d | 2015-11-05 01:07:54 +0000 | [diff] [blame] | 127 | if (!LLVMSymbolizerPathOrErr) |
| 128 | return false; |
| 129 | const std::string &LLVMSymbolizerPath = *LLVMSymbolizerPathOrErr; |
Reid Kleckner | ba5757d | 2015-11-05 01:07:54 +0000 | [diff] [blame] | 130 | |
Richard Smith | 2ad6d48 | 2016-06-09 00:53:21 +0000 | [diff] [blame] | 131 | // If we don't know argv0 or the address of main() at this point, try |
| 132 | // to guess it anyway (it's possible on some platforms). |
| 133 | std::string MainExecutableName = |
Sean Silva | de68a37 | 2019-04-15 22:07:56 +0000 | [diff] [blame] | 134 | sys::fs::exists(Argv0) ? (std::string)Argv0 |
| 135 | : sys::fs::getMainExecutable(nullptr, nullptr); |
Reid Kleckner | ba5757d | 2015-11-05 01:07:54 +0000 | [diff] [blame] | 136 | BumpPtrAllocator Allocator; |
| 137 | StringSaver StrPool(Allocator); |
| 138 | std::vector<const char *> Modules(Depth, nullptr); |
| 139 | std::vector<intptr_t> Offsets(Depth, 0); |
| 140 | if (!findModulesAndOffsets(StackTrace, Depth, Modules.data(), Offsets.data(), |
| 141 | MainExecutableName.c_str(), StrPool)) |
| 142 | return false; |
| 143 | int InputFD; |
| 144 | SmallString<32> InputFile, OutputFile; |
| 145 | sys::fs::createTemporaryFile("symbolizer-input", "", InputFD, InputFile); |
| 146 | sys::fs::createTemporaryFile("symbolizer-output", "", OutputFile); |
| 147 | FileRemover InputRemover(InputFile.c_str()); |
| 148 | FileRemover OutputRemover(OutputFile.c_str()); |
| 149 | |
| 150 | { |
| 151 | raw_fd_ostream Input(InputFD, true); |
| 152 | for (int i = 0; i < Depth; i++) { |
| 153 | if (Modules[i]) |
| 154 | Input << Modules[i] << " " << (void*)Offsets[i] << "\n"; |
| 155 | } |
| 156 | } |
| 157 | |
Zachary Turner | 08426e1 | 2018-06-12 17:43:52 +0000 | [diff] [blame] | 158 | Optional<StringRef> Redirects[] = {StringRef(InputFile), |
Alexandre Ganea | b536bf52 | 2018-12-18 18:23:36 +0000 | [diff] [blame] | 159 | StringRef(OutputFile), StringRef("")}; |
Zachary Turner | 08426e1 | 2018-06-12 17:43:52 +0000 | [diff] [blame] | 160 | StringRef Args[] = {"llvm-symbolizer", "--functions=linkage", "--inlining", |
Nico Weber | 712e8d2 | 2018-04-29 00:45:03 +0000 | [diff] [blame] | 161 | #ifdef _WIN32 |
Zachary Turner | 08426e1 | 2018-06-12 17:43:52 +0000 | [diff] [blame] | 162 | // Pass --relative-address on Windows so that we don't |
| 163 | // have to add ImageBase from PE file. |
| 164 | // FIXME: Make this the default for llvm-symbolizer. |
| 165 | "--relative-address", |
Reid Kleckner | ba5757d | 2015-11-05 01:07:54 +0000 | [diff] [blame] | 166 | #endif |
Zachary Turner | 08426e1 | 2018-06-12 17:43:52 +0000 | [diff] [blame] | 167 | "--demangle"}; |
Reid Kleckner | ba5757d | 2015-11-05 01:07:54 +0000 | [diff] [blame] | 168 | int RunResult = |
Zachary Turner | 08426e1 | 2018-06-12 17:43:52 +0000 | [diff] [blame] | 169 | sys::ExecuteAndWait(LLVMSymbolizerPath, Args, None, Redirects); |
Reid Kleckner | ba5757d | 2015-11-05 01:07:54 +0000 | [diff] [blame] | 170 | if (RunResult != 0) |
| 171 | return false; |
| 172 | |
| 173 | // This report format is based on the sanitizer stack trace printer. See |
| 174 | // sanitizer_stacktrace_printer.cc in compiler-rt. |
| 175 | auto OutputBuf = MemoryBuffer::getFile(OutputFile.c_str()); |
| 176 | if (!OutputBuf) |
| 177 | return false; |
| 178 | StringRef Output = OutputBuf.get()->getBuffer(); |
| 179 | SmallVector<StringRef, 32> Lines; |
| 180 | Output.split(Lines, "\n"); |
| 181 | auto CurLine = Lines.begin(); |
| 182 | int frame_no = 0; |
| 183 | for (int i = 0; i < Depth; i++) { |
Alexandre Ganea | b67d91e | 2018-12-18 18:13:13 +0000 | [diff] [blame] | 184 | auto PrintLineHeader = [&]() { |
| 185 | OS << right_justify(formatv("#{0}", frame_no++).str(), |
| 186 | std::log10(Depth) + 2) |
| 187 | << ' ' << format_ptr(StackTrace[i]) << ' '; |
| 188 | }; |
Reid Kleckner | ba5757d | 2015-11-05 01:07:54 +0000 | [diff] [blame] | 189 | if (!Modules[i]) { |
Alexandre Ganea | b67d91e | 2018-12-18 18:13:13 +0000 | [diff] [blame] | 190 | PrintLineHeader(); |
| 191 | OS << '\n'; |
Reid Kleckner | ba5757d | 2015-11-05 01:07:54 +0000 | [diff] [blame] | 192 | continue; |
| 193 | } |
| 194 | // Read pairs of lines (function name and file/line info) until we |
| 195 | // encounter empty line. |
| 196 | for (;;) { |
| 197 | if (CurLine == Lines.end()) |
| 198 | return false; |
| 199 | StringRef FunctionName = *CurLine++; |
| 200 | if (FunctionName.empty()) |
| 201 | break; |
Alexandre Ganea | b67d91e | 2018-12-18 18:13:13 +0000 | [diff] [blame] | 202 | PrintLineHeader(); |
Reid Kleckner | ba5757d | 2015-11-05 01:07:54 +0000 | [diff] [blame] | 203 | if (!FunctionName.startswith("??")) |
| 204 | OS << FunctionName << ' '; |
| 205 | if (CurLine == Lines.end()) |
| 206 | return false; |
| 207 | StringRef FileLineInfo = *CurLine++; |
| 208 | if (!FileLineInfo.startswith("??")) |
| 209 | OS << FileLineInfo; |
| 210 | else |
| 211 | OS << "(" << Modules[i] << '+' << format_hex(Offsets[i], 0) << ")"; |
| 212 | OS << "\n"; |
| 213 | } |
| 214 | } |
| 215 | return true; |
| 216 | } |
| 217 | |
Reid Spencer | 3d7a614 | 2004-08-29 19:22:48 +0000 | [diff] [blame] | 218 | // Include the platform-specific parts of this class. |
Reid Spencer | 844f3fe | 2004-12-27 06:16:11 +0000 | [diff] [blame] | 219 | #ifdef LLVM_ON_UNIX |
Reid Spencer | c892a0d | 2005-01-09 23:29:00 +0000 | [diff] [blame] | 220 | #include "Unix/Signals.inc" |
Reid Spencer | 844f3fe | 2004-12-27 06:16:11 +0000 | [diff] [blame] | 221 | #endif |
Nico Weber | 712e8d2 | 2018-04-29 00:45:03 +0000 | [diff] [blame] | 222 | #ifdef _WIN32 |
Michael J. Spencer | 447762d | 2010-11-29 18:16:10 +0000 | [diff] [blame] | 223 | #include "Windows/Signals.inc" |
Reid Spencer | 844f3fe | 2004-12-27 06:16:11 +0000 | [diff] [blame] | 224 | #endif |