Signals.cpp revision 360784
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#include "llvm/ADT/STLExtras.h"
16#include "llvm/ADT/StringRef.h"
17#include "llvm/Config/llvm-config.h"
18#include "llvm/Support/CommandLine.h"
19#include "llvm/Support/ErrorOr.h"
20#include "llvm/Support/FileSystem.h"
21#include "llvm/Support/FileUtilities.h"
22#include "llvm/Support/Format.h"
23#include "llvm/Support/FormatAdapters.h"
24#include "llvm/Support/FormatVariadic.h"
25#include "llvm/Support/ManagedStatic.h"
26#include "llvm/Support/MemoryBuffer.h"
27#include "llvm/Support/Mutex.h"
28#include "llvm/Support/Program.h"
29#include "llvm/Support/StringSaver.h"
30#include "llvm/Support/raw_ostream.h"
31#include <vector>
32
33//===----------------------------------------------------------------------===//
34//=== WARNING: Implementation here must contain only TRULY operating system
35//===          independent code.
36//===----------------------------------------------------------------------===//
37
38using namespace llvm;
39
40// Use explicit storage to avoid accessing cl::opt in a signal handler.
41static bool DisableSymbolicationFlag = false;
42static cl::opt<bool, true>
43    DisableSymbolication("disable-symbolication",
44                         cl::desc("Disable symbolizing crash backtraces."),
45                         cl::location(DisableSymbolicationFlag), cl::Hidden);
46
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.
50struct CallbackAndCookie {
51  sys::SignalHandlerCallback Callback;
52  void *Cookie;
53  enum class Status { Empty, Initializing, Initialized, Executing };
54  std::atomic<Status> Flag;
55};
56static constexpr size_t MaxSignalHandlerCallbacks = 8;
57static CallbackAndCookie CallBacksToRun[MaxSignalHandlerCallbacks];
58
59// Signal-safe.
60void sys::RunSignalHandlers() {
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.
75static 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);
86    return;
87  }
88  report_fatal_error("too many signal callbacks already registered");
89}
90
91static 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.
98static 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.
105LLVM_ATTRIBUTE_USED
106static bool printSymbolizedStackTrace(StringRef Argv0, void **StackTrace,
107                                      int Depth, llvm::raw_ostream &OS) {
108  if (DisableSymbolicationFlag)
109    return false;
110
111  // Don't recursively invoke the llvm-symbolizer binary.
112  if (Argv0.find("llvm-symbolizer") != std::string::npos)
113    return false;
114
115  // FIXME: Subtract necessary number from StackTrace entries to turn return addresses
116  // into actual instruction addresses.
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");
127  if (!LLVMSymbolizerPathOrErr)
128    return false;
129  const std::string &LLVMSymbolizerPath = *LLVMSymbolizerPathOrErr;
130
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 =
134      sys::fs::exists(Argv0) ? (std::string)Argv0
135                             : sys::fs::getMainExecutable(nullptr, nullptr);
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
158  Optional<StringRef> Redirects[] = {StringRef(InputFile),
159                                     StringRef(OutputFile), StringRef("")};
160  StringRef Args[] = {"llvm-symbolizer", "--functions=linkage", "--inlining",
161#ifdef _WIN32
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",
166#endif
167                      "--demangle"};
168  int RunResult =
169      sys::ExecuteAndWait(LLVMSymbolizerPath, Args, None, Redirects);
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++) {
184    auto PrintLineHeader = [&]() {
185      OS << right_justify(formatv("#{0}", frame_no++).str(),
186                          std::log10(Depth) + 2)
187         << ' ' << format_ptr(StackTrace[i]) << ' ';
188    };
189    if (!Modules[i]) {
190      PrintLineHeader();
191      OS << '\n';
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;
202      PrintLineHeader();
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
218// Include the platform-specific parts of this class.
219#ifdef LLVM_ON_UNIX
220#include "Unix/Signals.inc"
221#endif
222#ifdef _WIN32
223#include "Windows/Signals.inc"
224#endif
225