brintos

brintos / llvm-project-archived public Read only

0
0
Text · 14.0 KiB · f8a14a4 Raw
358 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9// This file defines some helpful functions for dealing with the possibility of10// 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 35//===----------------------------------------------------------------------===//36//=== WARNING: Implementation here must contain only TRULY operating system37//===          independent code.38//===----------------------------------------------------------------------===//39 40using namespace llvm;41 42// Use explicit storage to avoid accessing cl::opt in a signal handler.43static bool DisableSymbolicationFlag = false;44static ManagedStatic<std::string> CrashDiagnosticsDirectory;45namespace {46struct 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};54struct 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} // namespace63void 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 72constexpr char DisableSymbolizationEnv[] = "LLVM_DISABLE_SYMBOLIZATION";73constexpr char LLVMSymbolizerPathEnv[] = "LLVM_SYMBOLIZER_PATH";74constexpr char EnableSymbolizerMarkupEnv[] = "LLVM_ENABLE_SYMBOLIZER_MARKUP";75 76// Callbacks to run in signal handler must be lock-free because a signal handler77// could be running as we add new callbacks. We don't add unbounded numbers of78// callbacks, an array is therefore sufficient.79struct CallbackAndCookie {80  sys::SignalHandlerCallback Callback;81  void *Cookie;82  enum class Status { Empty, Initializing, Initialized, Executing };83  std::atomic<Status> Flag;84};85 86static constexpr size_t MaxSignalHandlerCallbacks = 8;87 88// A global array of CallbackAndCookie may not compile with89// -Werror=global-constructors in c++20 and above90static std::array<CallbackAndCookie, MaxSignalHandlerCallbacks> &91CallBacksToRun() {92  static std::array<CallbackAndCookie, MaxSignalHandlerCallbacks> callbacks;93  return callbacks;94}95 96// Signal-safe.97void sys::RunSignalHandlers() {98  for (CallbackAndCookie &RunMe : CallBacksToRun()) {99    auto Expected = CallbackAndCookie::Status::Initialized;100    auto Desired = CallbackAndCookie::Status::Executing;101    if (!RunMe.Flag.compare_exchange_strong(Expected, Desired))102      continue;103    (*RunMe.Callback)(RunMe.Cookie);104    RunMe.Callback = nullptr;105    RunMe.Cookie = nullptr;106    RunMe.Flag.store(CallbackAndCookie::Status::Empty);107  }108}109 110// Signal-safe.111static void insertSignalHandler(sys::SignalHandlerCallback FnPtr,112                                void *Cookie) {113  for (CallbackAndCookie &SetMe : CallBacksToRun()) {114    auto Expected = CallbackAndCookie::Status::Empty;115    auto Desired = CallbackAndCookie::Status::Initializing;116    if (!SetMe.Flag.compare_exchange_strong(Expected, Desired))117      continue;118    SetMe.Callback = FnPtr;119    SetMe.Cookie = Cookie;120    SetMe.Flag.store(CallbackAndCookie::Status::Initialized);121    return;122  }123  report_fatal_error("too many signal callbacks already registered");124}125 126static bool findModulesAndOffsets(void **StackTrace, int Depth,127                                  const char **Modules, intptr_t *Offsets,128                                  const char *MainExecutableName,129                                  StringSaver &StrPool);130 131/// Format a pointer value as hexadecimal. Zero pad it out so its always the132/// same width.133static FormattedNumber format_ptr(void *PC) {134  // Each byte is two hex digits plus 2 for the 0x prefix.135  unsigned PtrWidth = 2 + 2 * sizeof(void *);136  return format_hex((uint64_t)PC, PtrWidth);137}138 139/// Reads a file \p Filename written by llvm-symbolizer containing function140/// names and source locations for the addresses in \p AddressList and returns141/// the strings in a vector of pairs, where the first pair element is the index142/// of the corresponding entry in AddressList and the second is the symbolized143/// frame, in a format based on the sanitizer stack trace printer, with the144/// exception that it does not write out frame numbers (i.e. "#2 " for the145/// third address), as it is not assumed that \p AddressList corresponds to a146/// single stack trace.147/// There may be multiple returned entries for a single \p AddressList entry if148/// that frame address corresponds to one or more inlined frames; in this case,149/// all frames for an address will appear contiguously and in-order.150std::optional<SmallVector<std::pair<unsigned, std::string>, 0>>151collectAddressSymbols(void **AddressList, unsigned AddressCount,152                      const char *MainExecutableName,153                      const std::string &LLVMSymbolizerPath) {154  BumpPtrAllocator Allocator;155  StringSaver StrPool(Allocator);156  SmallVector<const char *, 0> Modules(AddressCount, nullptr);157  SmallVector<intptr_t, 0> Offsets(AddressCount, 0);158  if (!findModulesAndOffsets(AddressList, AddressCount, Modules.data(),159                             Offsets.data(), MainExecutableName, StrPool))160    return {};161  int InputFD;162  SmallString<32> InputFile, OutputFile;163  sys::fs::createTemporaryFile("symbolizer-input", "", InputFD, InputFile);164  sys::fs::createTemporaryFile("symbolizer-output", "", OutputFile);165  FileRemover InputRemover(InputFile.c_str());166  FileRemover OutputRemover(OutputFile.c_str());167 168  {169    raw_fd_ostream Input(InputFD, true);170    for (unsigned AddrIdx = 0; AddrIdx < AddressCount; AddrIdx++) {171      if (Modules[AddrIdx])172        Input << Modules[AddrIdx] << " " << (void *)Offsets[AddrIdx] << "\n";173    }174  }175 176  std::optional<StringRef> Redirects[] = {InputFile.str(), OutputFile.str(),177                                          StringRef("")};178  StringRef Args[] = {"llvm-symbolizer", "--functions=linkage", "--inlining",179#ifdef _WIN32180                      // Pass --relative-address on Windows so that we don't181                      // have to add ImageBase from PE file.182                      // FIXME: Make this the default for llvm-symbolizer.183                      "--relative-address",184#endif185                      "--demangle"};186  int RunResult =187      sys::ExecuteAndWait(LLVMSymbolizerPath, Args, std::nullopt, Redirects);188  if (RunResult != 0)189    return {};190 191  SmallVector<std::pair<unsigned, std::string>, 0> Result;192  auto OutputBuf = MemoryBuffer::getFile(OutputFile.c_str());193  if (!OutputBuf)194    return {};195  StringRef Output = OutputBuf.get()->getBuffer();196  SmallVector<StringRef, 32> Lines;197  Output.split(Lines, "\n");198  auto *CurLine = Lines.begin();199  // Lines contains the output from llvm-symbolizer, which should contain for200  // each address with a module in order of appearance, one or more lines201  // containing the function name and line associated with that address,202  // followed by an empty line.203  // For each address, adds an output entry for every real or inlined frame at204  // that address. For addresses without known modules, we have a single entry205  // containing just the formatted address; for all other output entries, we206  // output the function entry if it is known, and either the line number if it207  // is known or the module+address offset otherwise.208  for (unsigned AddrIdx = 0; AddrIdx < AddressCount; AddrIdx++) {209    if (!Modules[AddrIdx]) {210      auto &SymbolizedFrame = Result.emplace_back(std::make_pair(AddrIdx, ""));211      raw_string_ostream OS(SymbolizedFrame.second);212      OS << format_ptr(AddressList[AddrIdx]);213      continue;214    }215    // Read pairs of lines (function name and file/line info) until we216    // encounter empty line.217    for (;;) {218      if (CurLine == Lines.end())219        return {};220      StringRef FunctionName = *CurLine++;221      if (FunctionName.empty())222        break;223      auto &SymbolizedFrame = Result.emplace_back(std::make_pair(AddrIdx, ""));224      raw_string_ostream OS(SymbolizedFrame.second);225      OS << format_ptr(AddressList[AddrIdx]) << ' ';226      if (!FunctionName.starts_with("??"))227        OS << FunctionName << ' ';228      if (CurLine == Lines.end())229        return {};230      StringRef FileLineInfo = *CurLine++;231      if (!FileLineInfo.starts_with("??")) {232        OS << FileLineInfo;233      } else {234        OS << "(" << Modules[AddrIdx] << '+' << format_hex(Offsets[AddrIdx], 0)235           << ")";236      }237    }238  }239  return Result;240}241 242ErrorOr<std::string> getLLVMSymbolizerPath(StringRef Argv0 = {}) {243  ErrorOr<std::string> LLVMSymbolizerPathOrErr = std::error_code();244  if (const char *Path = getenv(LLVMSymbolizerPathEnv)) {245    LLVMSymbolizerPathOrErr = sys::findProgramByName(Path);246  } else if (!Argv0.empty()) {247    StringRef Parent = llvm::sys::path::parent_path(Argv0);248    if (!Parent.empty())249      LLVMSymbolizerPathOrErr =250          sys::findProgramByName("llvm-symbolizer", Parent);251  }252  if (!LLVMSymbolizerPathOrErr)253    LLVMSymbolizerPathOrErr = sys::findProgramByName("llvm-symbolizer");254  return LLVMSymbolizerPathOrErr;255}256 257/// Helper that launches llvm-symbolizer and symbolizes a backtrace.258LLVM_ATTRIBUTE_USED259static bool printSymbolizedStackTrace(StringRef Argv0, void **StackTrace,260                                      int Depth, llvm::raw_ostream &OS) {261  if (DisableSymbolicationFlag || getenv(DisableSymbolizationEnv))262    return false;263 264  // Don't recursively invoke the llvm-symbolizer binary.265  if (Argv0.contains("llvm-symbolizer"))266    return false;267 268  // FIXME: Subtract necessary number from StackTrace entries to turn return269  // addresses into actual instruction addresses.270  // Use llvm-symbolizer tool to symbolize the stack traces. First look for it271  // alongside our binary, then in $PATH.272  ErrorOr<std::string> LLVMSymbolizerPathOrErr = getLLVMSymbolizerPath(Argv0);273  if (!LLVMSymbolizerPathOrErr)274    return false;275  const std::string &LLVMSymbolizerPath = *LLVMSymbolizerPathOrErr;276 277  // If we don't know argv0 or the address of main() at this point, try278  // to guess it anyway (it's possible on some platforms).279  std::string MainExecutableName =280      sys::fs::exists(Argv0) ? std::string(Argv0)281                             : sys::fs::getMainExecutable(nullptr, nullptr);282 283  auto SymbolizedAddressesOpt = collectAddressSymbols(284      StackTrace, Depth, MainExecutableName.c_str(), LLVMSymbolizerPath);285  if (!SymbolizedAddressesOpt)286    return false;287  for (unsigned FrameNo = 0; FrameNo < SymbolizedAddressesOpt->size();288       ++FrameNo) {289    OS << right_justify(formatv("#{0}", FrameNo).str(), std::log10(Depth) + 2)290       << ' ' << (*SymbolizedAddressesOpt)[FrameNo].second << '\n';291  }292  return true;293}294 295#if LLVM_ENABLE_DEBUGLOC_TRACKING_ORIGIN296void sys::symbolizeAddresses(AddressSet &Addresses,297                             SymbolizedAddressMap &SymbolizedAddresses) {298  assert(!DisableSymbolicationFlag && !getenv(DisableSymbolizationEnv) &&299         "Debugify origin stacktraces require symbolization to be enabled.");300 301  // Convert Set of Addresses to ordered list.302  SmallVector<void *, 0> AddressList(Addresses.begin(), Addresses.end());303  if (AddressList.empty())304    return;305  llvm::sort(AddressList);306 307  // Use llvm-symbolizer tool to symbolize the stack traces. First look for it308  // alongside our binary, then in $PATH.309  ErrorOr<std::string> LLVMSymbolizerPathOrErr = getLLVMSymbolizerPath();310  if (!LLVMSymbolizerPathOrErr)311    report_fatal_error("Debugify origin stacktraces require llvm-symbolizer");312  const std::string &LLVMSymbolizerPath = *LLVMSymbolizerPathOrErr;313 314  // Try to guess the main executable name, since we don't have argv0 available315  // here.316  std::string MainExecutableName = sys::fs::getMainExecutable(nullptr, nullptr);317 318  auto SymbolizedAddressesOpt =319      collectAddressSymbols(AddressList.begin(), AddressList.size(),320                            MainExecutableName.c_str(), LLVMSymbolizerPath);321  if (!SymbolizedAddressesOpt)322    return;323  for (auto SymbolizedFrame : *SymbolizedAddressesOpt) {324    SmallVector<std::string, 0> &SymbolizedAddrs =325        SymbolizedAddresses[AddressList[SymbolizedFrame.first]];326    SymbolizedAddrs.push_back(SymbolizedFrame.second);327  }328  return;329}330#endif331 332static bool printMarkupContext(raw_ostream &OS, const char *MainExecutableName);333 334LLVM_ATTRIBUTE_USED335static bool printMarkupStackTrace(StringRef Argv0, void **StackTrace, int Depth,336                                  raw_ostream &OS) {337  const char *Env = getenv(EnableSymbolizerMarkupEnv);338  if (!Env || !*Env)339    return false;340 341  std::string MainExecutableName =342      sys::fs::exists(Argv0) ? std::string(Argv0)343                             : sys::fs::getMainExecutable(nullptr, nullptr);344  if (!printMarkupContext(OS, MainExecutableName.c_str()))345    return false;346  for (int I = 0; I < Depth; I++)347    OS << format("{{{bt:%d:%#016x}}}\n", I, StackTrace[I]);348  return true;349}350 351// Include the platform-specific parts of this class.352#ifdef LLVM_ON_UNIX353#include "Unix/Signals.inc"354#endif355#ifdef _WIN32356#include "Windows/Signals.inc"357#endif358