Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp
35231 views
1
//===-- llvm-symbolizer.cpp - Simple addr2line-like symbolizer ------------===//
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 utility works much like "addr2line". It is able of transforming
10
// tuples (module name, module offset) to code locations (function name,
11
// file, line number, column number). It is targeted for compiler-rt tools
12
// (especially AddressSanitizer and ThreadSanitizer) that can use it
13
// to symbolize stack traces in their error reports.
14
//
15
//===----------------------------------------------------------------------===//
16
17
#include "Opts.inc"
18
#include "llvm/ADT/StringExtras.h"
19
#include "llvm/ADT/StringRef.h"
20
#include "llvm/Config/config.h"
21
#include "llvm/DebugInfo/Symbolize/DIPrinter.h"
22
#include "llvm/DebugInfo/Symbolize/Markup.h"
23
#include "llvm/DebugInfo/Symbolize/MarkupFilter.h"
24
#include "llvm/DebugInfo/Symbolize/SymbolizableModule.h"
25
#include "llvm/DebugInfo/Symbolize/Symbolize.h"
26
#include "llvm/Debuginfod/BuildIDFetcher.h"
27
#include "llvm/Debuginfod/Debuginfod.h"
28
#include "llvm/Debuginfod/HTTPClient.h"
29
#include "llvm/Option/Arg.h"
30
#include "llvm/Option/ArgList.h"
31
#include "llvm/Option/Option.h"
32
#include "llvm/Support/COM.h"
33
#include "llvm/Support/CommandLine.h"
34
#include "llvm/Support/Debug.h"
35
#include "llvm/Support/Errc.h"
36
#include "llvm/Support/FileSystem.h"
37
#include "llvm/Support/LLVMDriver.h"
38
#include "llvm/Support/Path.h"
39
#include "llvm/Support/StringSaver.h"
40
#include "llvm/Support/WithColor.h"
41
#include "llvm/Support/raw_ostream.h"
42
#include <algorithm>
43
#include <cstdio>
44
#include <cstring>
45
#include <iostream>
46
#include <string>
47
48
using namespace llvm;
49
using namespace symbolize;
50
51
namespace {
52
enum ID {
53
OPT_INVALID = 0, // This is not an option ID.
54
#define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),
55
#include "Opts.inc"
56
#undef OPTION
57
};
58
59
#define PREFIX(NAME, VALUE) \
60
static constexpr StringLiteral NAME##_init[] = VALUE; \
61
static constexpr ArrayRef<StringLiteral> NAME(NAME##_init, \
62
std::size(NAME##_init) - 1);
63
#include "Opts.inc"
64
#undef PREFIX
65
66
using namespace llvm::opt;
67
static constexpr opt::OptTable::Info InfoTable[] = {
68
#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),
69
#include "Opts.inc"
70
#undef OPTION
71
};
72
73
class SymbolizerOptTable : public opt::GenericOptTable {
74
public:
75
SymbolizerOptTable() : GenericOptTable(InfoTable) {
76
setGroupedShortOptions(true);
77
}
78
};
79
} // namespace
80
81
static std::string ToolName;
82
83
static void printError(const ErrorInfoBase &EI, StringRef AuxInfo) {
84
WithColor::error(errs(), ToolName);
85
if (!AuxInfo.empty())
86
errs() << "'" << AuxInfo << "': ";
87
EI.log(errs());
88
errs() << '\n';
89
}
90
91
template <typename T>
92
static void print(const Request &Request, Expected<T> &ResOrErr,
93
DIPrinter &Printer) {
94
if (ResOrErr) {
95
// No error, print the result.
96
Printer.print(Request, *ResOrErr);
97
return;
98
}
99
100
// Handle the error.
101
bool PrintEmpty = true;
102
handleAllErrors(std::move(ResOrErr.takeError()),
103
[&](const ErrorInfoBase &EI) {
104
PrintEmpty = Printer.printError(Request, EI);
105
});
106
107
if (PrintEmpty)
108
Printer.print(Request, T());
109
}
110
111
enum class OutputStyle { LLVM, GNU, JSON };
112
113
enum class Command {
114
Code,
115
Data,
116
Frame,
117
};
118
119
static void enableDebuginfod(LLVMSymbolizer &Symbolizer,
120
const opt::ArgList &Args) {
121
static bool IsEnabled = false;
122
if (IsEnabled)
123
return;
124
IsEnabled = true;
125
// Look up symbols using the debuginfod client.
126
Symbolizer.setBuildIDFetcher(std::make_unique<DebuginfodFetcher>(
127
Args.getAllArgValues(OPT_debug_file_directory_EQ)));
128
// The HTTPClient must be initialized for use by the debuginfod client.
129
HTTPClient::initialize();
130
}
131
132
static StringRef getSpaceDelimitedWord(StringRef &Source) {
133
const char kDelimiters[] = " \n\r";
134
const char *Pos = Source.data();
135
StringRef Result;
136
Pos += strspn(Pos, kDelimiters);
137
if (*Pos == '"' || *Pos == '\'') {
138
char Quote = *Pos;
139
Pos++;
140
const char *End = strchr(Pos, Quote);
141
if (!End)
142
return StringRef();
143
Result = StringRef(Pos, End - Pos);
144
Pos = End + 1;
145
} else {
146
int NameLength = strcspn(Pos, kDelimiters);
147
Result = StringRef(Pos, NameLength);
148
Pos += NameLength;
149
}
150
Source = StringRef(Pos, Source.end() - Pos);
151
return Result;
152
}
153
154
static Error makeStringError(StringRef Msg) {
155
return make_error<StringError>(Msg, inconvertibleErrorCode());
156
}
157
158
static Error parseCommand(StringRef BinaryName, bool IsAddr2Line,
159
StringRef InputString, Command &Cmd,
160
std::string &ModuleName, object::BuildID &BuildID,
161
StringRef &Symbol, uint64_t &Offset) {
162
ModuleName = BinaryName;
163
if (InputString.consume_front("CODE ")) {
164
Cmd = Command::Code;
165
} else if (InputString.consume_front("DATA ")) {
166
Cmd = Command::Data;
167
} else if (InputString.consume_front("FRAME ")) {
168
Cmd = Command::Frame;
169
} else {
170
// If no cmd, assume it's CODE.
171
Cmd = Command::Code;
172
}
173
174
// Parse optional input file specification.
175
bool HasFilePrefix = false;
176
bool HasBuildIDPrefix = false;
177
while (!InputString.empty()) {
178
InputString = InputString.ltrim();
179
if (InputString.consume_front("FILE:")) {
180
if (HasFilePrefix || HasBuildIDPrefix)
181
return makeStringError("duplicate input file specification prefix");
182
HasFilePrefix = true;
183
continue;
184
}
185
if (InputString.consume_front("BUILDID:")) {
186
if (HasBuildIDPrefix || HasFilePrefix)
187
return makeStringError("duplicate input file specification prefix");
188
HasBuildIDPrefix = true;
189
continue;
190
}
191
break;
192
}
193
194
// If an input file is not specified on the command line, try to extract it
195
// from the command.
196
if (HasBuildIDPrefix || HasFilePrefix) {
197
InputString = InputString.ltrim();
198
if (InputString.empty()) {
199
if (HasFilePrefix)
200
return makeStringError("must be followed by an input file");
201
else
202
return makeStringError("must be followed by a hash");
203
}
204
205
if (!BinaryName.empty() || !BuildID.empty())
206
return makeStringError("input file has already been specified");
207
208
StringRef Name = getSpaceDelimitedWord(InputString);
209
if (Name.empty())
210
return makeStringError("unbalanced quotes in input file name");
211
if (HasBuildIDPrefix) {
212
BuildID = parseBuildID(Name);
213
if (BuildID.empty())
214
return makeStringError("wrong format of build-id");
215
} else {
216
ModuleName = Name;
217
}
218
} else if (BinaryName.empty() && BuildID.empty()) {
219
// No input file has been specified. If the input string contains at least
220
// two items, assume that the first item is a file name.
221
ModuleName = getSpaceDelimitedWord(InputString);
222
if (ModuleName.empty())
223
return makeStringError("no input filename has been specified");
224
}
225
226
// Parse address specification, which can be an offset in module or a
227
// symbol with optional offset.
228
InputString = InputString.trim();
229
if (InputString.empty())
230
return makeStringError("no module offset has been specified");
231
232
// If input string contains a space, ignore everything after it. This behavior
233
// is consistent with GNU addr2line.
234
int AddrSpecLength = InputString.find_first_of(" \n\r");
235
StringRef AddrSpec = InputString.substr(0, AddrSpecLength);
236
bool StartsWithDigit = std::isdigit(AddrSpec.front());
237
238
// GNU addr2line assumes the address is hexadecimal and allows a redundant
239
// "0x" or "0X" prefix; do the same for compatibility.
240
if (IsAddr2Line)
241
AddrSpec.consume_front("0x") || AddrSpec.consume_front("0X");
242
243
// If address specification is a number, treat it as a module offset.
244
if (!AddrSpec.getAsInteger(IsAddr2Line ? 16 : 0, Offset)) {
245
// Module offset is an address.
246
Symbol = StringRef();
247
return Error::success();
248
}
249
250
// If address specification starts with a digit, but is not a number, consider
251
// it as invalid.
252
if (StartsWithDigit || AddrSpec.empty())
253
return makeStringError("expected a number as module offset");
254
255
// Otherwise it is a symbol name, potentially with an offset.
256
Symbol = AddrSpec;
257
Offset = 0;
258
259
// If the address specification contains '+', try treating it as
260
// "symbol + offset".
261
size_t Plus = AddrSpec.rfind('+');
262
if (Plus != StringRef::npos) {
263
StringRef SymbolStr = AddrSpec.take_front(Plus);
264
StringRef OffsetStr = AddrSpec.substr(Plus + 1);
265
if (!SymbolStr.empty() && !OffsetStr.empty() &&
266
!OffsetStr.getAsInteger(0, Offset)) {
267
Symbol = SymbolStr;
268
return Error::success();
269
}
270
// The found '+' is not an offset delimiter.
271
}
272
273
return Error::success();
274
}
275
276
template <typename T>
277
void executeCommand(StringRef ModuleName, const T &ModuleSpec, Command Cmd,
278
StringRef Symbol, uint64_t Offset, uint64_t AdjustVMA,
279
bool ShouldInline, OutputStyle Style,
280
LLVMSymbolizer &Symbolizer, DIPrinter &Printer) {
281
uint64_t AdjustedOffset = Offset - AdjustVMA;
282
object::SectionedAddress Address = {AdjustedOffset,
283
object::SectionedAddress::UndefSection};
284
Request SymRequest = {
285
ModuleName, Symbol.empty() ? std::make_optional(Offset) : std::nullopt,
286
Symbol};
287
if (Cmd == Command::Data) {
288
Expected<DIGlobal> ResOrErr = Symbolizer.symbolizeData(ModuleSpec, Address);
289
print(SymRequest, ResOrErr, Printer);
290
} else if (Cmd == Command::Frame) {
291
Expected<std::vector<DILocal>> ResOrErr =
292
Symbolizer.symbolizeFrame(ModuleSpec, Address);
293
print(SymRequest, ResOrErr, Printer);
294
} else if (!Symbol.empty()) {
295
Expected<std::vector<DILineInfo>> ResOrErr =
296
Symbolizer.findSymbol(ModuleSpec, Symbol, Offset);
297
print(SymRequest, ResOrErr, Printer);
298
} else if (ShouldInline) {
299
Expected<DIInliningInfo> ResOrErr =
300
Symbolizer.symbolizeInlinedCode(ModuleSpec, Address);
301
print(SymRequest, ResOrErr, Printer);
302
} else if (Style == OutputStyle::GNU) {
303
// With PrintFunctions == FunctionNameKind::LinkageName (default)
304
// and UseSymbolTable == true (also default), Symbolizer.symbolizeCode()
305
// may override the name of an inlined function with the name of the topmost
306
// caller function in the inlining chain. This contradicts the existing
307
// behavior of addr2line. Symbolizer.symbolizeInlinedCode() overrides only
308
// the topmost function, which suits our needs better.
309
Expected<DIInliningInfo> ResOrErr =
310
Symbolizer.symbolizeInlinedCode(ModuleSpec, Address);
311
Expected<DILineInfo> Res0OrErr =
312
!ResOrErr
313
? Expected<DILineInfo>(ResOrErr.takeError())
314
: ((ResOrErr->getNumberOfFrames() == 0) ? DILineInfo()
315
: ResOrErr->getFrame(0));
316
print(SymRequest, Res0OrErr, Printer);
317
} else {
318
Expected<DILineInfo> ResOrErr =
319
Symbolizer.symbolizeCode(ModuleSpec, Address);
320
print(SymRequest, ResOrErr, Printer);
321
}
322
Symbolizer.pruneCache();
323
}
324
325
static void printUnknownLineInfo(std::string ModuleName, DIPrinter &Printer) {
326
Request SymRequest = {ModuleName, std::nullopt, StringRef()};
327
Printer.print(SymRequest, DILineInfo());
328
}
329
330
static void symbolizeInput(const opt::InputArgList &Args,
331
object::BuildIDRef IncomingBuildID,
332
uint64_t AdjustVMA, bool IsAddr2Line,
333
OutputStyle Style, StringRef InputString,
334
LLVMSymbolizer &Symbolizer, DIPrinter &Printer) {
335
Command Cmd;
336
std::string ModuleName;
337
object::BuildID BuildID(IncomingBuildID.begin(), IncomingBuildID.end());
338
uint64_t Offset = 0;
339
StringRef Symbol;
340
341
// An empty input string may be used to check if the process is alive and
342
// responding to input. Do not emit a message on stderr in this case but
343
// respond on stdout.
344
if (InputString.empty()) {
345
printUnknownLineInfo(ModuleName, Printer);
346
return;
347
}
348
if (Error E = parseCommand(Args.getLastArgValue(OPT_obj_EQ), IsAddr2Line,
349
StringRef(InputString), Cmd, ModuleName, BuildID,
350
Symbol, Offset)) {
351
handleAllErrors(std::move(E), [&](const StringError &EI) {
352
printError(EI, InputString);
353
printUnknownLineInfo(ModuleName, Printer);
354
});
355
return;
356
}
357
bool ShouldInline = Args.hasFlag(OPT_inlines, OPT_no_inlines, !IsAddr2Line);
358
if (!BuildID.empty()) {
359
assert(ModuleName.empty());
360
if (!Args.hasArg(OPT_no_debuginfod))
361
enableDebuginfod(Symbolizer, Args);
362
std::string BuildIDStr = toHex(BuildID);
363
executeCommand(BuildIDStr, BuildID, Cmd, Symbol, Offset, AdjustVMA,
364
ShouldInline, Style, Symbolizer, Printer);
365
} else {
366
executeCommand(ModuleName, ModuleName, Cmd, Symbol, Offset, AdjustVMA,
367
ShouldInline, Style, Symbolizer, Printer);
368
}
369
}
370
371
static void printHelp(StringRef ToolName, const SymbolizerOptTable &Tbl,
372
raw_ostream &OS) {
373
const char HelpText[] = " [options] addresses...";
374
Tbl.printHelp(OS, (ToolName + HelpText).str().c_str(),
375
ToolName.str().c_str());
376
// TODO Replace this with OptTable API once it adds extrahelp support.
377
OS << "\nPass @FILE as argument to read options from FILE.\n";
378
}
379
380
static opt::InputArgList parseOptions(int Argc, char *Argv[], bool IsAddr2Line,
381
StringSaver &Saver,
382
SymbolizerOptTable &Tbl) {
383
StringRef ToolName = IsAddr2Line ? "llvm-addr2line" : "llvm-symbolizer";
384
// The environment variable specifies initial options which can be overridden
385
// by commnad line options.
386
Tbl.setInitialOptionsFromEnvironment(IsAddr2Line ? "LLVM_ADDR2LINE_OPTS"
387
: "LLVM_SYMBOLIZER_OPTS");
388
bool HasError = false;
389
opt::InputArgList Args =
390
Tbl.parseArgs(Argc, Argv, OPT_UNKNOWN, Saver, [&](StringRef Msg) {
391
errs() << ("error: " + Msg + "\n");
392
HasError = true;
393
});
394
if (HasError)
395
exit(1);
396
if (Args.hasArg(OPT_help)) {
397
printHelp(ToolName, Tbl, outs());
398
exit(0);
399
}
400
if (Args.hasArg(OPT_version)) {
401
outs() << ToolName << '\n';
402
cl::PrintVersionMessage();
403
exit(0);
404
}
405
406
return Args;
407
}
408
409
template <typename T>
410
static void parseIntArg(const opt::InputArgList &Args, int ID, T &Value) {
411
if (const opt::Arg *A = Args.getLastArg(ID)) {
412
StringRef V(A->getValue());
413
if (!llvm::to_integer(V, Value, 0)) {
414
errs() << A->getSpelling() +
415
": expected a non-negative integer, but got '" + V + "'";
416
exit(1);
417
}
418
} else {
419
Value = 0;
420
}
421
}
422
423
static FunctionNameKind decideHowToPrintFunctions(const opt::InputArgList &Args,
424
bool IsAddr2Line) {
425
if (Args.hasArg(OPT_functions))
426
return FunctionNameKind::LinkageName;
427
if (const opt::Arg *A = Args.getLastArg(OPT_functions_EQ))
428
return StringSwitch<FunctionNameKind>(A->getValue())
429
.Case("none", FunctionNameKind::None)
430
.Case("short", FunctionNameKind::ShortName)
431
.Default(FunctionNameKind::LinkageName);
432
return IsAddr2Line ? FunctionNameKind::None : FunctionNameKind::LinkageName;
433
}
434
435
static std::optional<bool> parseColorArg(const opt::InputArgList &Args) {
436
if (Args.hasArg(OPT_color))
437
return true;
438
if (const opt::Arg *A = Args.getLastArg(OPT_color_EQ))
439
return StringSwitch<std::optional<bool>>(A->getValue())
440
.Case("always", true)
441
.Case("never", false)
442
.Case("auto", std::nullopt);
443
return std::nullopt;
444
}
445
446
static object::BuildID parseBuildIDArg(const opt::InputArgList &Args, int ID) {
447
const opt::Arg *A = Args.getLastArg(ID);
448
if (!A)
449
return {};
450
451
StringRef V(A->getValue());
452
object::BuildID BuildID = parseBuildID(V);
453
if (BuildID.empty()) {
454
errs() << A->getSpelling() + ": expected a build ID, but got '" + V + "'\n";
455
exit(1);
456
}
457
return BuildID;
458
}
459
460
// Symbolize markup from stdin and write the result to stdout.
461
static void filterMarkup(const opt::InputArgList &Args, LLVMSymbolizer &Symbolizer) {
462
MarkupFilter Filter(outs(), Symbolizer, parseColorArg(Args));
463
std::string InputString;
464
while (std::getline(std::cin, InputString)) {
465
InputString += '\n';
466
Filter.filter(std::move(InputString));
467
}
468
Filter.finish();
469
}
470
471
int llvm_symbolizer_main(int argc, char **argv, const llvm::ToolContext &) {
472
sys::InitializeCOMRAII COM(sys::COMThreadingMode::MultiThreaded);
473
474
ToolName = argv[0];
475
bool IsAddr2Line = sys::path::stem(ToolName).contains("addr2line");
476
BumpPtrAllocator A;
477
StringSaver Saver(A);
478
SymbolizerOptTable Tbl;
479
opt::InputArgList Args = parseOptions(argc, argv, IsAddr2Line, Saver, Tbl);
480
481
LLVMSymbolizer::Options Opts;
482
uint64_t AdjustVMA;
483
PrinterConfig Config;
484
parseIntArg(Args, OPT_adjust_vma_EQ, AdjustVMA);
485
if (const opt::Arg *A = Args.getLastArg(OPT_basenames, OPT_relativenames)) {
486
Opts.PathStyle =
487
A->getOption().matches(OPT_basenames)
488
? DILineInfoSpecifier::FileLineInfoKind::BaseNameOnly
489
: DILineInfoSpecifier::FileLineInfoKind::RelativeFilePath;
490
} else {
491
Opts.PathStyle = DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath;
492
}
493
Opts.DebugFileDirectory = Args.getAllArgValues(OPT_debug_file_directory_EQ);
494
Opts.DefaultArch = Args.getLastArgValue(OPT_default_arch_EQ).str();
495
Opts.Demangle = Args.hasFlag(OPT_demangle, OPT_no_demangle, !IsAddr2Line);
496
Opts.DWPName = Args.getLastArgValue(OPT_dwp_EQ).str();
497
Opts.FallbackDebugPath =
498
Args.getLastArgValue(OPT_fallback_debug_path_EQ).str();
499
Opts.PrintFunctions = decideHowToPrintFunctions(Args, IsAddr2Line);
500
parseIntArg(Args, OPT_print_source_context_lines_EQ,
501
Config.SourceContextLines);
502
Opts.RelativeAddresses = Args.hasArg(OPT_relative_address);
503
Opts.UntagAddresses =
504
Args.hasFlag(OPT_untag_addresses, OPT_no_untag_addresses, !IsAddr2Line);
505
Opts.UseDIA = Args.hasArg(OPT_use_dia);
506
#if !defined(LLVM_ENABLE_DIA_SDK)
507
if (Opts.UseDIA) {
508
WithColor::warning() << "DIA not available; using native PDB reader\n";
509
Opts.UseDIA = false;
510
}
511
#endif
512
Opts.UseSymbolTable = true;
513
if (Args.hasArg(OPT_cache_size_EQ))
514
parseIntArg(Args, OPT_cache_size_EQ, Opts.MaxCacheSize);
515
Config.PrintAddress = Args.hasArg(OPT_addresses);
516
Config.PrintFunctions = Opts.PrintFunctions != FunctionNameKind::None;
517
Config.Pretty = Args.hasArg(OPT_pretty_print);
518
Config.Verbose = Args.hasArg(OPT_verbose);
519
520
for (const opt::Arg *A : Args.filtered(OPT_dsym_hint_EQ)) {
521
StringRef Hint(A->getValue());
522
if (sys::path::extension(Hint) == ".dSYM") {
523
Opts.DsymHints.emplace_back(Hint);
524
} else {
525
errs() << "Warning: invalid dSYM hint: \"" << Hint
526
<< "\" (must have the '.dSYM' extension).\n";
527
}
528
}
529
530
LLVMSymbolizer Symbolizer(Opts);
531
532
if (Args.hasFlag(OPT_debuginfod, OPT_no_debuginfod, canUseDebuginfod()))
533
enableDebuginfod(Symbolizer, Args);
534
535
if (Args.hasArg(OPT_filter_markup)) {
536
filterMarkup(Args, Symbolizer);
537
return 0;
538
}
539
540
auto Style = IsAddr2Line ? OutputStyle::GNU : OutputStyle::LLVM;
541
if (const opt::Arg *A = Args.getLastArg(OPT_output_style_EQ)) {
542
if (strcmp(A->getValue(), "GNU") == 0)
543
Style = OutputStyle::GNU;
544
else if (strcmp(A->getValue(), "JSON") == 0)
545
Style = OutputStyle::JSON;
546
else
547
Style = OutputStyle::LLVM;
548
}
549
550
if (Args.hasArg(OPT_build_id_EQ) && Args.hasArg(OPT_obj_EQ)) {
551
errs() << "error: cannot specify both --build-id and --obj\n";
552
return EXIT_FAILURE;
553
}
554
object::BuildID BuildID = parseBuildIDArg(Args, OPT_build_id_EQ);
555
556
std::unique_ptr<DIPrinter> Printer;
557
if (Style == OutputStyle::GNU)
558
Printer = std::make_unique<GNUPrinter>(outs(), printError, Config);
559
else if (Style == OutputStyle::JSON)
560
Printer = std::make_unique<JSONPrinter>(outs(), Config);
561
else
562
Printer = std::make_unique<LLVMPrinter>(outs(), printError, Config);
563
564
// When an input file is specified, exit immediately if the file cannot be
565
// read. If getOrCreateModuleInfo succeeds, symbolizeInput will reuse the
566
// cached file handle.
567
if (auto *Arg = Args.getLastArg(OPT_obj_EQ); Arg) {
568
auto Status = Symbolizer.getOrCreateModuleInfo(Arg->getValue());
569
if (!Status) {
570
Request SymRequest = {Arg->getValue(), 0, StringRef()};
571
handleAllErrors(Status.takeError(), [&](const ErrorInfoBase &EI) {
572
Printer->printError(SymRequest, EI);
573
});
574
return EXIT_FAILURE;
575
}
576
}
577
578
std::vector<std::string> InputAddresses = Args.getAllArgValues(OPT_INPUT);
579
if (InputAddresses.empty()) {
580
const int kMaxInputStringLength = 1024;
581
char InputString[kMaxInputStringLength];
582
583
while (fgets(InputString, sizeof(InputString), stdin)) {
584
// Strip newline characters.
585
std::string StrippedInputString(InputString);
586
llvm::erase_if(StrippedInputString,
587
[](char c) { return c == '\r' || c == '\n'; });
588
symbolizeInput(Args, BuildID, AdjustVMA, IsAddr2Line, Style,
589
StrippedInputString, Symbolizer, *Printer);
590
outs().flush();
591
}
592
} else {
593
Printer->listBegin();
594
for (StringRef Address : InputAddresses)
595
symbolizeInput(Args, BuildID, AdjustVMA, IsAddr2Line, Style, Address,
596
Symbolizer, *Printer);
597
Printer->listEnd();
598
}
599
600
return 0;
601
}
602
603