Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
## Handle invalid argument.
# RUN: llvm-symbolizer "DATA tmp.o Z" --output-style=JSON | \
# RUN: FileCheck %s --check-prefix=INVARG --strict-whitespace --match-full-lines --implicit-check-not={{.}}
# INVARG:[{"Error":{"Message":"unable to parse arguments: DATA tmp.o Z"},"ModuleName":"tmp.o"}]
# INVARG:[{"ModuleName":"tmp.o","Symbol":[{"Column":0,"Discriminator":0,"FileName":"","FunctionName":"","Line":0,"StartAddress":"","StartFileName":"","StartLine":0}]}]

# RUN: llvm-mc -filetype=obj -triple=x86_64-pc-linux %s -o %t.o

Expand Down
2 changes: 1 addition & 1 deletion llvm/test/tools/llvm-symbolizer/output-style-json-frame.ll
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
;; Handle invalid argument.
; RUN: llvm-symbolizer "FRAME tmp.o Z" --output-style=JSON | \
; RUN: FileCheck %s --check-prefix=INVARG --strict-whitespace --match-full-lines --implicit-check-not={{.}}
; INVARG:[{"Error":{"Message":"unable to parse arguments: FRAME tmp.o Z"},"ModuleName":"tmp.o"}]
; INVARG:[{"ModuleName":"tmp.o","Symbol":[{"Column":0,"Discriminator":0,"FileName":"","FunctionName":"","Line":0,"StartAddress":"","StartFileName":"","StartLine":0}]}]

; RUN: llc -filetype=obj -o %t.o %s

Expand Down
4 changes: 2 additions & 2 deletions llvm/test/tools/llvm-symbolizer/sym-verbose.test
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

RUN: llvm-symbolizer --verbose --print-address --obj=%p/Inputs/discrim < %p/Inputs/discrim.inp | FileCheck %s

CHECK: some text
CHECK: ??

CHECK: 0x400590
CHECK-NEXT: foo
Expand Down Expand Up @@ -118,4 +118,4 @@ CHECK-NEXT: Line: 10
CHECK-NEXT: Column: 0
CHECK-NEXT: Discriminator: 2

CHECK: some more text
CHECK: ??
19 changes: 11 additions & 8 deletions llvm/test/tools/llvm-symbolizer/sym.test
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ RUN: llvm-symbolizer --print-address --obj=%p/Inputs/addr.exe < %p/Inputs/addr.i
RUN: llvm-symbolizer --addresses --obj=%p/Inputs/addr.exe < %p/Inputs/addr.inp | FileCheck %s
RUN: llvm-symbolizer -a --obj=%p/Inputs/addr.exe < %p/Inputs/addr.inp | FileCheck %s

CHECK: some text
CHECK: ??:0:0
CHECK-EMPTY:
CHECK-NEXT: 0x40054d
CHECK-NEXT: inctwo
CHECK-NEXT: {{[/\]+}}tmp{{[/\]+}}x.c:3:3
Expand All @@ -30,7 +31,8 @@ CHECK-NEXT: {{[/\]+}}tmp{{[/\]+}}x.c:7:0
CHECK-NEXT: main
CHECK-NEXT: {{[/\]+}}tmp{{[/\]+}}x.c:14:0
CHECK-EMPTY:
CHECK-NEXT: some text2
CHECK-NEXT: ??
CHECK-NEXT: ??:0:0

RUN: llvm-symbolizer --inlining --print-address --pretty-print --obj=%p/Inputs/addr.exe < %p/Inputs/addr.inp | FileCheck -check-prefix="PRETTY" %s
RUN: llvm-symbolizer --inlining --print-address -p --obj=%p/Inputs/addr.exe < %p/Inputs/addr.inp | FileCheck -check-prefix="PRETTY" %s
Expand All @@ -43,11 +45,11 @@ RUN: llvm-symbolizer -i --print-address -p --obj=%p/Inputs/addr.exe < %p/Inputs/
# Support this compatibility alias for a while.
RUN: llvm-symbolizer --inlining=true --print-address -p --obj=%p/Inputs/addr.exe < %p/Inputs/addr.inp | FileCheck -check-prefix="PRETTY" %s

PRETTY: some text
PRETTY: ??:0:0
PRETTY: {{[0x]+}}40054d: inctwo at {{[/\]+}}tmp{{[/\]+}}x.c:3:3
PRETTY: (inlined by) inc at {{[/\]+}}tmp{{[/\]+}}x.c:7:0
PRETTY: (inlined by) main at {{[/\]+}}tmp{{[/\]+}}x.c:14:0
PRETTY: some text2
PRETTY: ??:0:0

RUN: llvm-addr2line --obj=%p/Inputs/addr.exe < %p/Inputs/addr.inp | FileCheck -check-prefix=A2L %s
RUN: llvm-addr2line -a --obj=%p/Inputs/addr.exe < %p/Inputs/addr.inp | FileCheck -check-prefixes=A2L,A2L_A %s
Expand All @@ -62,22 +64,23 @@ RUN: llvm-addr2line -pai --obj=%p/Inputs/addr.exe < %p/Inputs/addr.inp | FileChe
RUN: llvm-addr2line -pfi --obj=%p/Inputs/addr.exe < %p/Inputs/addr.inp | FileCheck -check-prefixes=A2LP,A2LP_F,A2LP_FI %s
RUN: llvm-addr2line -pafi --obj=%p/Inputs/addr.exe < %p/Inputs/addr.inp | FileCheck -check-prefixes=A2LP,A2LP_AF,A2LP_FI %s

A2L: some text
A2L: ??:0
A2L_A-NEXT: 0x40054d
A2L_F-NEXT: inctwo
A2L-NEXT: {{[/\]+}}tmp{{[/\]+}}x.c:3{{$}}
A2L_FI-NEXT: inc{{$}}
A2L_I-NEXT: {{[/\]+}}tmp{{[/\]+}}x.c:7{{$}}
A2L_FI-NEXT: main
A2L_I-NEXT: {{[/\]+}}tmp{{[/\]+}}x.c:14{{$}}
A2L-NEXT: some text2
A2L_F-NEXT: ??
A2L-NEXT: ??:0

A2LP: some text
A2LP: ??:0
A2LP_A-NEXT: 0x40054d: {{[/\]+}}tmp{{[/\]+}}x.c:3{{$}}
A2LP_F-NEXT: inctwo at {{[/\]+}}tmp{{[/\]+}}x.c:3{{$}}
A2LP_AF-NEXT: 0x40054d: inctwo at {{[/\]+}}tmp{{[/\]+}}x.c:3{{$}}
A2LP_I-NEXT: {{[/\]+}}tmp{{[/\]+}}x.c:7{{$}}
A2LP_I-NEXT: {{[/\]+}}tmp{{[/\]+}}x.c:14{{$}}
A2LP_FI-NEXT: (inlined by) inc at {{[/\]+}}tmp{{[/\]+}}x.c:7{{$}}
A2LP_FI-NEXT: (inlined by) main at {{[/\]+}}tmp{{[/\]+}}x.c:14{{$}}
A2LP-NEXT: some text2
A2LP-NEXT: ??:0
81 changes: 56 additions & 25 deletions llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "llvm/Support/COM.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/LLVMDriver.h"
Expand Down Expand Up @@ -80,10 +81,10 @@ class SymbolizerOptTable : public opt::GenericOptTable {

static std::string ToolName;

static void printError(const ErrorInfoBase &EI, StringRef Path) {
static void printError(const ErrorInfoBase &EI, StringRef AuxInfo) {
WithColor::error(errs(), ToolName);
if (!EI.isA<FileError>())
errs() << "'" << Path << "': ";
if (!AuxInfo.empty())
errs() << "'" << AuxInfo << "': ";
EI.log(errs());
errs() << '\n';
}
Expand Down Expand Up @@ -151,10 +152,14 @@ static StringRef getSpaceDelimitedWord(StringRef &Source) {
return Result;
}

static bool parseCommand(StringRef BinaryName, bool IsAddr2Line,
StringRef InputString, Command &Cmd,
std::string &ModuleName, object::BuildID &BuildID,
uint64_t &ModuleOffset) {
static Error makeStringError(StringRef Msg) {
return make_error<StringError>(Msg, inconvertibleErrorCode());
}

static Error parseCommand(StringRef BinaryName, bool IsAddr2Line,
StringRef InputString, Command &Cmd,
std::string &ModuleName, object::BuildID &BuildID,
uint64_t &ModuleOffset) {
ModuleName = BinaryName;
if (InputString.consume_front("CODE ")) {
Cmd = Command::Code;
Expand All @@ -174,55 +179,69 @@ static bool parseCommand(StringRef BinaryName, bool IsAddr2Line,
InputString = InputString.ltrim();
if (InputString.consume_front("FILE:")) {
if (HasFilePrefix || HasBuildIDPrefix)
// Input file specification prefix has already been seen.
return false;
return makeStringError("duplicate input file specification prefix");
HasFilePrefix = true;
continue;
}
if (InputString.consume_front("BUILDID:")) {
if (HasBuildIDPrefix || HasFilePrefix)
// Input file specification prefix has already been seen.
return false;
return makeStringError("duplicate input file specification prefix");
HasBuildIDPrefix = true;
continue;
}
break;
}

// If an input file is not specified on the command line, try to extract it
// from the command.
if (HasBuildIDPrefix || HasFilePrefix) {
InputString = InputString.ltrim();
if (InputString.empty()) {
if (HasFilePrefix)
return makeStringError("must be followed by an input file");
else
return makeStringError("must be followed by a hash");
}

if (!BinaryName.empty() || !BuildID.empty())
// Input file has already been specified on the command line.
return false;
return makeStringError("input file has already been specified");

StringRef Name = getSpaceDelimitedWord(InputString);
if (Name.empty())
// Wrong name for module file.
return false;
return makeStringError("unbalanced quotes in input file name");
if (HasBuildIDPrefix) {
BuildID = parseBuildID(Name);
if (BuildID.empty())
// Wrong format of BuildID hash.
return false;
return makeStringError("wrong format of build-id");
} else {
ModuleName = Name;
}
} else if (BinaryName.empty() && BuildID.empty()) {
// No input file has been specified. If the input string contains at least
// two items, assume that the first item is a file name.
ModuleName = getSpaceDelimitedWord(InputString);
if (ModuleName.empty() || InputString.empty())
// No input filename has been specified.
return false;
if (ModuleName.empty())
return makeStringError("no input filename has been specified");
}

// Skip delimiters and parse module offset.
// Parse module offset.
InputString = InputString.ltrim();
if (InputString.empty())
return makeStringError("no module offset has been specified");
int OffsetLength = InputString.find_first_of(" \n\r");
StringRef Offset = InputString.substr(0, OffsetLength);
// GNU addr2line assumes the offset is hexadecimal and allows a redundant
// "0x" or "0X" prefix; do the same for compatibility.
if (IsAddr2Line)
Offset.consume_front("0x") || Offset.consume_front("0X");
return !Offset.getAsInteger(IsAddr2Line ? 16 : 0, ModuleOffset);

// If the input is not a valid module offset, it is not an error, but its
// lookup does not make sense. Return error of different kind to distinguish
// from error or success.
if (Offset.getAsInteger(IsAddr2Line ? 16 : 0, ModuleOffset))
return errorCodeToError(errc::invalid_argument);

return Error::success();
}

template <typename T>
Expand Down Expand Up @@ -268,6 +287,11 @@ void executeCommand(StringRef ModuleName, const T &ModuleSpec, Command Cmd,
Symbolizer.pruneCache();
}

static void printUnknownLineInfo(std::string ModuleName, DIPrinter &Printer) {
Request SymRequest = {ModuleName, std::nullopt};
Printer.print(SymRequest, DILineInfo());
}

static void symbolizeInput(const opt::InputArgList &Args,
object::BuildIDRef IncomingBuildID,
uint64_t AdjustVMA, bool IsAddr2Line,
Expand All @@ -277,9 +301,16 @@ static void symbolizeInput(const opt::InputArgList &Args,
std::string ModuleName;
object::BuildID BuildID(IncomingBuildID.begin(), IncomingBuildID.end());
uint64_t Offset = 0;
if (!parseCommand(Args.getLastArgValue(OPT_obj_EQ), IsAddr2Line,
StringRef(InputString), Cmd, ModuleName, BuildID, Offset)) {
Printer.printInvalidCommand({ModuleName, std::nullopt}, InputString);
if (Error E = parseCommand(Args.getLastArgValue(OPT_obj_EQ), IsAddr2Line,
StringRef(InputString), Cmd, ModuleName, BuildID,
Offset)) {
handleAllErrors(
std::move(E),
[&](const StringError &EI) {
printError(EI, InputString);
printUnknownLineInfo(ModuleName, Printer);
},
[&](const ECError &EI) { printUnknownLineInfo(ModuleName, Printer); });
return;
}
bool ShouldInline = Args.hasFlag(OPT_inlines, OPT_no_inlines, !IsAddr2Line);
Expand Down