llvm-symbolizer.cpp revision 280031
1//===-- llvm-symbolizer.cpp - Simple addr2line-like symbolizer ------------===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This utility works much like "addr2line". It is able of transforming
11// tuples (module name, module offset) to code locations (function name,
12// file, line number, column number). It is targeted for compiler-rt tools
13// (especially AddressSanitizer and ThreadSanitizer) that can use it
14// to symbolize stack traces in their error reports.
15//
16//===----------------------------------------------------------------------===//
17
18#include "LLVMSymbolize.h"
19#include "llvm/ADT/StringRef.h"
20#include "llvm/Support/CommandLine.h"
21#include "llvm/Support/Debug.h"
22#include "llvm/Support/FileSystem.h"
23#include "llvm/Support/ManagedStatic.h"
24#include "llvm/Support/PrettyStackTrace.h"
25#include "llvm/Support/Signals.h"
26#include "llvm/Support/raw_ostream.h"
27#include <cstdio>
28#include <cstring>
29#include <string>
30
31using namespace llvm;
32using namespace symbolize;
33
34static cl::opt<bool>
35ClUseSymbolTable("use-symbol-table", cl::init(true),
36                 cl::desc("Prefer names in symbol table to names "
37                          "in debug info"));
38
39static cl::opt<FunctionNameKind> ClPrintFunctions(
40    "functions", cl::init(FunctionNameKind::LinkageName),
41    cl::desc("Print function name for a given address:"),
42    cl::values(clEnumValN(FunctionNameKind::None, "none", "omit function name"),
43               clEnumValN(FunctionNameKind::ShortName, "short",
44                          "print short function name"),
45               clEnumValN(FunctionNameKind::LinkageName, "linkage",
46                          "print function linkage name"),
47               clEnumValEnd));
48
49static cl::opt<bool>
50ClPrintInlining("inlining", cl::init(true),
51                cl::desc("Print all inlined frames for a given address"));
52
53static cl::opt<bool>
54ClDemangle("demangle", cl::init(true), cl::desc("Demangle function names"));
55
56static cl::opt<std::string> ClDefaultArch("default-arch", cl::init(""),
57                                          cl::desc("Default architecture "
58                                                   "(for multi-arch objects)"));
59
60static cl::opt<std::string>
61ClBinaryName("obj", cl::init(""),
62             cl::desc("Path to object file to be symbolized (if not provided, "
63                      "object file should be specified for each input line)"));
64
65static cl::list<std::string>
66ClDsymHint("dsym-hint", cl::ZeroOrMore,
67           cl::desc("Path to .dSYM bundles to search for debug info for the "
68                    "object files"));
69
70static bool parseCommand(bool &IsData, std::string &ModuleName,
71                         uint64_t &ModuleOffset) {
72  const char *kDataCmd = "DATA ";
73  const char *kCodeCmd = "CODE ";
74  const int kMaxInputStringLength = 1024;
75  const char kDelimiters[] = " \n";
76  char InputString[kMaxInputStringLength];
77  if (!fgets(InputString, sizeof(InputString), stdin))
78    return false;
79  IsData = false;
80  ModuleName = "";
81  char *pos = InputString;
82  if (strncmp(pos, kDataCmd, strlen(kDataCmd)) == 0) {
83    IsData = true;
84    pos += strlen(kDataCmd);
85  } else if (strncmp(pos, kCodeCmd, strlen(kCodeCmd)) == 0) {
86    IsData = false;
87    pos += strlen(kCodeCmd);
88  } else {
89    // If no cmd, assume it's CODE.
90    IsData = false;
91  }
92  // Skip delimiters and parse input filename (if needed).
93  if (ClBinaryName == "") {
94    pos += strspn(pos, kDelimiters);
95    if (*pos == '"' || *pos == '\'') {
96      char quote = *pos;
97      pos++;
98      char *end = strchr(pos, quote);
99      if (!end)
100        return false;
101      ModuleName = std::string(pos, end - pos);
102      pos = end + 1;
103    } else {
104      int name_length = strcspn(pos, kDelimiters);
105      ModuleName = std::string(pos, name_length);
106      pos += name_length;
107    }
108  } else {
109    ModuleName = ClBinaryName;
110  }
111  // Skip delimiters and parse module offset.
112  pos += strspn(pos, kDelimiters);
113  int offset_length = strcspn(pos, kDelimiters);
114  if (StringRef(pos, offset_length).getAsInteger(0, ModuleOffset))
115    return false;
116  return true;
117}
118
119int main(int argc, char **argv) {
120  // Print stack trace if we signal out.
121  sys::PrintStackTraceOnErrorSignal();
122  PrettyStackTraceProgram X(argc, argv);
123  llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
124
125  cl::ParseCommandLineOptions(argc, argv, "llvm-symbolizer\n");
126  LLVMSymbolizer::Options Opts(ClUseSymbolTable, ClPrintFunctions,
127                               ClPrintInlining, ClDemangle, ClDefaultArch);
128  for (const auto &hint : ClDsymHint) {
129    if (sys::path::extension(hint) == ".dSYM") {
130      Opts.DsymHints.push_back(hint);
131    } else {
132      errs() << "Warning: invalid dSYM hint: \"" << hint <<
133                "\" (must have the '.dSYM' extension).\n";
134    }
135  }
136  LLVMSymbolizer Symbolizer(Opts);
137
138  bool IsData = false;
139  std::string ModuleName;
140  uint64_t ModuleOffset;
141  while (parseCommand(IsData, ModuleName, ModuleOffset)) {
142    std::string Result =
143        IsData ? Symbolizer.symbolizeData(ModuleName, ModuleOffset)
144               : Symbolizer.symbolizeCode(ModuleName, ModuleOffset);
145    outs() << Result << "\n";
146    outs().flush();
147  }
148  return 0;
149}
150