diff --git a/llvm/docs/CommandGuide/llvm-symbolizer.rst b/llvm/docs/CommandGuide/llvm-symbolizer.rst --- a/llvm/docs/CommandGuide/llvm-symbolizer.rst +++ b/llvm/docs/CommandGuide/llvm-symbolizer.rst @@ -18,8 +18,8 @@ If no address is specified on the command-line, it reads the addresses from standard input. If no input name is specified on the command-line, but addresses -are, or if at any time an input value is not recognized, the input is simply -echoed to the output. +are, the first address value is treated as an input name. If an input value is not +recognized, it reports that source information is not found. Input names can be specified together with the addresses either on standard input or as positional arguments on the command-line. By default, input names diff --git a/llvm/docs/ReleaseNotes.rst b/llvm/docs/ReleaseNotes.rst --- a/llvm/docs/ReleaseNotes.rst +++ b/llvm/docs/ReleaseNotes.rst @@ -160,6 +160,9 @@ Changes to the LLVM tools --------------------------------- +* llvm-symbolizer now treats invalid input as an address for which source + information is not found. + Changes to LLDB --------------------------------- diff --git a/llvm/include/llvm/DebugInfo/Symbolize/DIPrinter.h b/llvm/include/llvm/DebugInfo/Symbolize/DIPrinter.h --- a/llvm/include/llvm/DebugInfo/Symbolize/DIPrinter.h +++ b/llvm/include/llvm/DebugInfo/Symbolize/DIPrinter.h @@ -47,9 +47,6 @@ virtual void print(const Request &Request, const std::vector &Locals) = 0; - virtual void printInvalidCommand(const Request &Request, - StringRef Command) = 0; - virtual bool printError(const Request &Request, const ErrorInfoBase &ErrorInfo) = 0; @@ -83,7 +80,7 @@ virtual void printFooter() {} private: - void printHeader(uint64_t Address); + void printHeader(std::optional Address); public: PlainPrinterBase(raw_ostream &OS, ErrorHandler EH, PrinterConfig &Config) @@ -95,8 +92,6 @@ void print(const Request &Request, const std::vector &Locals) override; - void printInvalidCommand(const Request &Request, StringRef Command) override; - bool printError(const Request &Request, const ErrorInfoBase &ErrorInfo) override; @@ -147,8 +142,6 @@ void print(const Request &Request, const std::vector &Locals) override; - void printInvalidCommand(const Request &Request, StringRef Command) override; - bool printError(const Request &Request, const ErrorInfoBase &ErrorInfo) override; diff --git a/llvm/lib/DebugInfo/Symbolize/DIPrinter.cpp b/llvm/lib/DebugInfo/Symbolize/DIPrinter.cpp --- a/llvm/lib/DebugInfo/Symbolize/DIPrinter.cpp +++ b/llvm/lib/DebugInfo/Symbolize/DIPrinter.cpp @@ -105,10 +105,10 @@ } }; -void PlainPrinterBase::printHeader(uint64_t Address) { - if (Config.PrintAddress) { +void PlainPrinterBase::printHeader(std::optional Address) { + if (Address.has_value() && Config.PrintAddress) { OS << "0x"; - OS.write_hex(Address); + OS.write_hex(*Address); StringRef Delimiter = Config.Pretty ? ": " : "\n"; OS << Delimiter; } @@ -182,7 +182,7 @@ } void PlainPrinterBase::print(const Request &Request, const DILineInfo &Info) { - printHeader(*Request.Address); + printHeader(Request.Address); print(Info, false); printFooter(); } @@ -260,11 +260,6 @@ printFooter(); } -void PlainPrinterBase::printInvalidCommand(const Request &Request, - StringRef Command) { - OS << Command << '\n'; -} - bool PlainPrinterBase::printError(const Request &Request, const ErrorInfoBase &ErrorInfo) { ErrHandler(ErrorInfo, Request.ModuleName); @@ -367,13 +362,6 @@ printJSON(std::move(Json)); } -void JSONPrinter::printInvalidCommand(const Request &Request, - StringRef Command) { - printError(Request, - StringError("unable to parse arguments: " + Command, - std::make_error_code(std::errc::invalid_argument))); -} - bool JSONPrinter::printError(const Request &Request, const ErrorInfoBase &ErrorInfo) { json::Object Json = toJSON(Request, ErrorInfo.message()); diff --git a/llvm/test/Support/interrupts.test b/llvm/test/Support/interrupts.test --- a/llvm/test/Support/interrupts.test +++ b/llvm/test/Support/interrupts.test @@ -12,7 +12,7 @@ def run_symbolizer(): proc = subprocess.Popen([sys.argv[2]], stdout=subprocess.PIPE, stdin=subprocess.PIPE) # Write then read some output to ensure the process has started fully. - proc.stdin.write(b'foo\n') + proc.stdin.write(b'foo bar\n') proc.stdin.flush() proc.stdout.readline() # Windows handles signals differently. diff --git a/llvm/test/tools/llvm-symbolizer/debuginfod.test b/llvm/test/tools/llvm-symbolizer/debuginfod.test --- a/llvm/test/tools/llvm-symbolizer/debuginfod.test +++ b/llvm/test/tools/llvm-symbolizer/debuginfod.test @@ -39,9 +39,9 @@ # Passing BUILDID twice is a syntax error. RUN: env DEBUGINFOD_CACHE_PATH=%t llvm-symbolizer \ -RUN: "BUILDID:BUILDID:127da749021c1fc1a58cba734a1f542cbe2b7ce4 0x40054d" | \ +RUN: "BUILDID:BUILDID:127da749021c1fc1a58cba734a1f542cbe2b7ce4 0x40054d" 2>&1 | \ RUN: FileCheck %s --check-prefix=BUILDIDBUILDID -BUILDIDBUILDID: BUILDID:BUILDID:127da749021c1fc1a58cba734a1f542cbe2b7ce4 0x40054d +BUILDIDBUILDID: error: 'BUILDID:BUILDID:127da749021c1fc1a58cba734a1f542cbe2b7ce4 0x40054d': duplicate input file specification prefix # CODE should work preceding build ID. RUN: env DEBUGINFOD_CACHE_PATH=%t llvm-symbolizer \ @@ -63,16 +63,16 @@ # BUILDID shouldn't be parsed if --obj is given, just like regular filenames. RUN: env DEBUGINFOD_CACHE_PATH=%t llvm-symbolizer \ RUN: --obj=%t/addr.exe \ -RUN: "BUILDID:127da749021c1fc1a58cba734a1f542cbe2b7ce4 0x40054d" | \ +RUN: "BUILDID:127da749021c1fc1a58cba734a1f542cbe2b7ce4 0x40054d" 2>&1 | \ RUN: FileCheck %s --check-prefix=BUILDIDIGNORED -BUILDIDIGNORED: BUILDID:127da749021c1fc1a58cba734a1f542cbe2b7ce4 0x40054d +BUILDIDIGNORED: error: 'BUILDID:127da749021c1fc1a58cba734a1f542cbe2b7ce4 0x40054d': input file has already been specified # Providing both BUILDID and FILE is a syntax error. RUN: env DEBUGINFOD_CACHE_PATH=%t llvm-symbolizer \ -RUN: "BUILDID:FILE:127da749021c1fc1a58cba734a1f542cbe2b7ce4 0x40054d" | \ +RUN: "BUILDID:FILE:127da749021c1fc1a58cba734a1f542cbe2b7ce4 0x40054d" 2>&1 | \ RUN: FileCheck %s --check-prefix=BUILDIDFILE -BUILDIDFILE: BUILDID:FILE:127da749021c1fc1a58cba734a1f542cbe2b7ce4 0x40054d +BUILDIDFILE: error: 'BUILDID:FILE:127da749021c1fc1a58cba734a1f542cbe2b7ce4 0x40054d': duplicate input file specification prefix RUN: env DEBUGINFOD_CACHE_PATH=%t llvm-symbolizer \ -RUN: "FILE:BUILDID:127da749021c1fc1a58cba734a1f542cbe2b7ce4 0x40054d" | \ +RUN: "FILE:BUILDID:127da749021c1fc1a58cba734a1f542cbe2b7ce4 0x40054d" 2>&1 | \ RUN: FileCheck %s --check-prefix=FILEBUILDID -FILEBUILDID: FILE:BUILDID:127da749021c1fc1a58cba734a1f542cbe2b7ce4 0x40054d +FILEBUILDID: error: 'FILE:BUILDID:127da749021c1fc1a58cba734a1f542cbe2b7ce4 0x40054d': duplicate input file specification prefix diff --git a/llvm/test/tools/llvm-symbolizer/file-prefix.test b/llvm/test/tools/llvm-symbolizer/file-prefix.test --- a/llvm/test/tools/llvm-symbolizer/file-prefix.test +++ b/llvm/test/tools/llvm-symbolizer/file-prefix.test @@ -4,6 +4,8 @@ FOUND: {{[/\]+}}tmp{{[/\]+}}x.c:14:0 # Passing FILE twice is a syntax error. -RUN: llvm-symbolizer "CODE FILE:FILE:%p/Inputs/addr.exe 0x40054d" | \ -RUN: FileCheck %s --check-prefix=FILEFILE -FILEFILE: CODE FILE:FILE:{{.*}}/Inputs/addr.exe 0x40054d +RUN: llvm-symbolizer "CODE FILE:FILE:%p/Inputs/addr.exe 0x40054d" 2>%t.err | FileCheck --check-prefix=NOTFOUND %s +RUN: FileCheck %s --check-prefix=FILEFILE --input-file %t.err +NOTFOUND: ?? +NOTFOUND-NEXT: ??:0:0 +FILEFILE: error: 'CODE FILE:FILE:{{.*}}/Inputs/addr.exe 0x40054d': duplicate input file specification prefix diff --git a/llvm/test/tools/llvm-symbolizer/flag-grouping.test b/llvm/test/tools/llvm-symbolizer/flag-grouping.test --- a/llvm/test/tools/llvm-symbolizer/flag-grouping.test +++ b/llvm/test/tools/llvm-symbolizer/flag-grouping.test @@ -3,8 +3,8 @@ RUN: llvm-symbolizer -apCie=%p/Inputs/addr.exe < %p/Inputs/addr.inp | FileCheck %s RUN: llvm-symbolizer -apCie%p/Inputs/addr.exe < %p/Inputs/addr.inp | FileCheck %s -CHECK: some text +CHECK: ?? at ??:0:0 CHECK: 0x40054d: inctwo CHECK: (inlined by) inc CHECK (inlined by) main -CHECK: some text2 +CHECK: ?? at ??:0:0 diff --git a/llvm/test/tools/llvm-symbolizer/flush-output.s b/llvm/test/tools/llvm-symbolizer/flush-output.s --- a/llvm/test/tools/llvm-symbolizer/flush-output.s +++ b/llvm/test/tools/llvm-symbolizer/flush-output.s @@ -14,4 +14,4 @@ # RUN: | FileCheck %s # CHECK: flush-output.s:10 -# CHECK: bad +# CHECK: ??:0 diff --git a/llvm/test/tools/llvm-symbolizer/functions.s b/llvm/test/tools/llvm-symbolizer/functions.s --- a/llvm/test/tools/llvm-symbolizer/functions.s +++ b/llvm/test/tools/llvm-symbolizer/functions.s @@ -26,7 +26,8 @@ # NONE-NOT: foo # NONE: functions.cpp:2:0 -# ERR: none +# ERR: ?? +# ERR-NEXT: ??:0 # The assembly below is a stripped down version of the output of: # clang -S -g --target=x86_64-pc-linux diff --git a/llvm/test/tools/llvm-symbolizer/get-input-file.test b/llvm/test/tools/llvm-symbolizer/get-input-file.test new file mode 100644 --- /dev/null +++ b/llvm/test/tools/llvm-symbolizer/get-input-file.test @@ -0,0 +1,47 @@ +# If binary input file is not specified, llvm-symbolizer assumes it is the first +# item in the command. + +# No input items at all, complain about missing input file. +RUN: echo | llvm-symbolizer 2>%t.1.err | FileCheck %s --check-prefix=NOSOURCE +RUN: FileCheck --input-file=%t.1.err --check-prefix=NOFILE %s + +# Only one input item, complain about missing addresses. +RUN: llvm-symbolizer "foo" 2>%t.2.err | FileCheck %s --check-prefix=NOSOURCE +RUN: FileCheck --input-file=%t.2.err --check-prefix=NOADDR %s + +# Two items specified, check if the first one is an existing file. +RUN: llvm-symbolizer "foo 400" 2>%t.3.err | FileCheck %s --check-prefix=NOSOURCE +RUN: FileCheck --input-file=%t.3.err --check-prefix=NOTFOUND %s + +# FILE: must be followed by a file name. +RUN: llvm-symbolizer "FILE:" 2>%t.4.err | FileCheck %s --check-prefix=NOSOURCE +RUN: FileCheck --input-file=%t.4.err --check-prefix=MISSING-FILE %s + +# BUILDID: must be followed by a hash. +RUN: llvm-symbolizer "BUILDID:" 2>%t.5.err | FileCheck %s --check-prefix=NOSOURCE +RUN: FileCheck --input-file=%t.5.err --check-prefix=MISSING-HASH %s + +# Wrong build-id. +RUN: llvm-symbolizer "BUILDID: foo" 2>%t.6.err | FileCheck %s --check-prefix=NOSOURCE +RUN: FileCheck --input-file=%t.6.err --check-prefix=BAD-HASH %s + +# Unbalanced string quotes in the file name. +RUN: llvm-symbolizer "FILE:\"foo" 2>%t.7.err | FileCheck %s --check-prefix=NOSOURCE +RUN: FileCheck --input-file=%t.7.err --check-prefix=BAD-QUOTE %s + +NOSOURCE: ?? +NOSOURCE-NEXT: ??:0:0 + +NOFILE: error: no input filename has been specified + +NOADDR: error: 'foo': no module offset has been specified + +NOTFOUND: error: 'foo': {{[nN]}}o such file or directory + +MISSING-FILE: error: 'FILE:': must be followed by an input file + +MISSING-HASH: error: 'BUILDID:': must be followed by a hash + +BAD-HASH: error: 'BUILDID: foo': wrong format of build-id + +BAD-QUOTE: error: 'FILE:"foo': unbalanced quotes in input file name diff --git a/llvm/test/tools/llvm-symbolizer/input-base.test b/llvm/test/tools/llvm-symbolizer/input-base.test --- a/llvm/test/tools/llvm-symbolizer/input-base.test +++ b/llvm/test/tools/llvm-symbolizer/input-base.test @@ -8,7 +8,7 @@ RUN: llvm-symbolizer -e %p/Inputs/addr.exe -a 0o11064 | FileCheck %s # llvm-symbolizer / StringRef::getAsInteger only accepts the 0o prefix in lowercase. -RUN: llvm-symbolizer -e %p/Inputs/addr.exe -a 0O1234 | FileCheck %s --check-prefix=INVALID-NOT-OCTAL-UPPER +RUN: llvm-symbolizer -e %p/Inputs/addr.exe -a 0O1234 | FileCheck %s --check-prefix=INVALID # llvm-addr2line always requires hexadecimal, but accepts an optional 0x prefix. RUN: llvm-addr2line -e %p/Inputs/addr.exe -a 0x1234 | FileCheck %s @@ -17,17 +17,16 @@ RUN: llvm-addr2line -e %p/Inputs/addr.exe -a 01234 | FileCheck %s RUN: llvm-addr2line -e %p/Inputs/addr.exe -a 0b1010 | FileCheck %s --check-prefix=HEXADECIMAL-NOT-BINARY RUN: llvm-addr2line -e %p/Inputs/addr.exe -a 0B1010 | FileCheck %s --check-prefix=HEXADECIMAL-NOT-BINARY -RUN: llvm-addr2line -e %p/Inputs/addr.exe -a 0o1234 | FileCheck %s --check-prefix=INVALID-NOT-OCTAL-LOWER -RUN: llvm-addr2line -e %p/Inputs/addr.exe -a 0O1234 | FileCheck %s --check-prefix=INVALID-NOT-OCTAL-UPPER +RUN: llvm-addr2line -e %p/Inputs/addr.exe -a 0o1234 | FileCheck %s --check-prefix=INVALID-NOT-OCTAL +RUN: llvm-addr2line -e %p/Inputs/addr.exe -a 0O1234 | FileCheck %s --check-prefix=INVALID-NOT-OCTAL CHECK: 0x1234 CHECK-NEXT: ?? +INVALID: ?? +INVALID-NEXT: ??:0 + HEXADECIMAL-NOT-BINARY: 0xb1010 HEXADECIMAL-NOT-BINARY: ?? -INVALID-NOT-OCTAL-LOWER: 0o1234 -INVALID-NOT-OCTAL-LOWER-NOT: ?? - -INVALID-NOT-OCTAL-UPPER: 0O1234 -INVALID-NOT-OCTAL-UPPER-NOT: ?? +INVALID-NOT-OCTAL: ??:0 diff --git a/llvm/test/tools/llvm-symbolizer/invalid-input-address.test b/llvm/test/tools/llvm-symbolizer/invalid-input-address.test --- a/llvm/test/tools/llvm-symbolizer/invalid-input-address.test +++ b/llvm/test/tools/llvm-symbolizer/invalid-input-address.test @@ -1,10 +1,12 @@ # Use address that can't fit in a 64-bit number. Show that llvm-symbolizer -# simply echoes it as per other malformed input addresses. +# simply treats it as an unknown symbol. RUN: llvm-symbolizer --obj=%p/Inputs/addr.exe 0x10000000000000000 | FileCheck --check-prefix=LARGE-ADDR %s -LARGE-ADDR-NOT: {{.}} -LARGE-ADDR: 0x10000000000000000 -LARGE-ADDR-NOT: {{.}} +LARGE-ADDR-NOT: {{.}} +LARGE-ADDR: ?? +LARGE-ADDR-NEXT: ??:0:0 +LARGE-ADDR-EMPTY: +LARGE-ADDR-NOT: {{.}} RUN: echo '"some text"' '"some text2"' > %t.rsp RUN: echo -e 'some text\nsome text2\n' > %t.inp @@ -19,5 +21,5 @@ RUN: llvm-addr2line --obj=%p/Inputs/addr.exe "some text" "some text2" | FileCheck --check-prefix=BAD-INPUT %s RUN: llvm-addr2line --obj=%p/Inputs/addr.exe @%t.rsp | FileCheck --check-prefix=BAD-INPUT %s -BAD-INPUT: some text -BAD-INPUT-NEXT: some text2 +BAD-INPUT: ?? +BAD-INPUT-NEXT: ??:0 diff --git a/llvm/test/tools/llvm-symbolizer/options-from-env.test b/llvm/test/tools/llvm-symbolizer/options-from-env.test --- a/llvm/test/tools/llvm-symbolizer/options-from-env.test +++ b/llvm/test/tools/llvm-symbolizer/options-from-env.test @@ -1,6 +1,16 @@ -# RUN: env LLVM_SYMBOLIZER_OPTS='0 1 --verbose' llvm-symbolizer 2 | FileCheck %s -# RUN: env LLVM_ADDR2LINE_OPTS='0 1 --verbose' llvm-addr2line 2 | FileCheck %s +RUN: env LLVM_SYMBOLIZER_OPTS='-e %p/Inputs/discrim --verbose' llvm-symbolizer 0x400590 | FileCheck --check-prefix=LLVM %s +RUN: env LLVM_ADDR2LINE_OPTS='-e %p/Inputs/discrim --verbose' llvm-addr2line 0x400590 | FileCheck --check-prefix=GNU %s -# CHECK: 0 -# CHECK-NEXT: 1 -# CHECK-NEXT: 2 +LLVM: foo +LLVM-NEXT: Filename: /tmp{{[\\/]}}discrim.c +LLVM-NEXT: Function start filename: /tmp{{[\\/]}}discrim.c +LLVM-NEXT: Function start line: 4 +LLVM-NEXT: Function start address: 0x400590 +LLVM-NEXT: Line: 5 +LLVM-NEXT: Column: 7 + +GNU: Filename: /tmp{{[\\/]}}discrim.c +GNU-NEXT: Function start filename: /tmp{{[\\/]}}discrim.c +GNU-NEXT: Function start line: 4 +GNU-NEXT: Line: 5 +GNU-NEXT: Column: 7 diff --git a/llvm/test/tools/llvm-symbolizer/output-style-empty-line.test b/llvm/test/tools/llvm-symbolizer/output-style-empty-line.test --- a/llvm/test/tools/llvm-symbolizer/output-style-empty-line.test +++ b/llvm/test/tools/llvm-symbolizer/output-style-empty-line.test @@ -13,7 +13,7 @@ LLVM: x.c:14:0 LLVM-EMPTY: -LLVM-NEXT: some text2 +LLVM-NEXT: ?? RUN: llvm-symbolizer --output-style=GNU -e %p/Inputs/addr.exe < %p/Inputs/addr.inp \ RUN: | FileCheck %s --check-prefix=GNU @@ -25,4 +25,4 @@ RUN: | FileCheck %s --check-prefix=GNU GNU: x.c:14 -GNU-NEXT: some text2 +GNU-NEXT: ?? diff --git a/llvm/test/tools/llvm-symbolizer/output-style-json-code.test b/llvm/test/tools/llvm-symbolizer/output-style-json-code.test --- a/llvm/test/tools/llvm-symbolizer/output-style-json-code.test +++ b/llvm/test/tools/llvm-symbolizer/output-style-json-code.test @@ -25,39 +25,39 @@ # RUN: llvm-symbolizer --output-style=JSON --no-inlines -e %p/Inputs/addr.exe < %p/Inputs/addr.inp | \ # RUN: FileCheck %s --check-prefix=NO-INLINES --strict-whitespace --match-full-lines --implicit-check-not={{.}} ## Invalid first argument before any valid one. -# NO-INLINES:{"Error":{"Message":"unable to parse arguments: some text"},"ModuleName":"{{.*}}/Inputs/addr.exe"} +# NO-INLINES:{"ModuleName":"{{.*}}/Inputs/addr.exe","Symbol":[{"Column":0,"Discriminator":0,"FileName":"","FunctionName":"","Line":0,"StartAddress":"","StartFileName":"","StartLine":0}]} ## Resolve valid address. # NO-INLINES-NEXT:{"Address":"0x40054d","ModuleName":"{{.*}}/Inputs/addr.exe","Symbol":[{"Column":3,"Discriminator":0,"FileName":"/tmp{{/|\\\\}}x.c","FunctionName":"main","Line":3,"StartAddress":"0x400540","StartFileName":"/tmp{{/|\\\\}}x.c","StartLine":2}]} ## Invalid argument after a valid one. -# NO-INLINES-NEXT:{"Error":{"Message":"unable to parse arguments: some text2"},"ModuleName":"{{.*}}/Inputs/addr.exe"} +# NO-INLINES-NEXT:{"ModuleName":"{{.*}}/Inputs/addr.exe","Symbol":[{"Column":0,"Discriminator":0,"FileName":"","FunctionName":"","Line":0,"StartAddress":"","StartFileName":"","StartLine":0}]} ## This test case is testing stdin input, inlines by default. # RUN: llvm-symbolizer --output-style=JSON -e %p/Inputs/addr.exe < %p/Inputs/addr.inp | \ # RUN: FileCheck %s --check-prefix=INLINE --strict-whitespace --match-full-lines --implicit-check-not={{.}} ## Invalid first argument before any valid one. -# INLINE:{"Error":{"Message":"unable to parse arguments: some text"},"ModuleName":"{{.*}}/Inputs/addr.exe"} +# INLINE:{"ModuleName":"{{.*}}/Inputs/addr.exe","Symbol":[{"Column":0,"Discriminator":0,"FileName":"","FunctionName":"","Line":0,"StartAddress":"","StartFileName":"","StartLine":0}]} ## Resolve valid address. # INLINE-NEXT:{"Address":"0x40054d","ModuleName":"{{.*}}/Inputs/addr.exe","Symbol":[{"Column":3,"Discriminator":0,"FileName":"/tmp{{/|\\\\}}x.c","FunctionName":"inctwo","Line":3,"StartAddress":"0x400540","StartFileName":"/tmp{{/|\\\\}}x.c","StartLine":2},{"Column":0,"Discriminator":0,"FileName":"/tmp{{/|\\\\}}x.c","FunctionName":"inc","Line":7,"StartAddress":"0x400540","StartFileName":"/tmp{{/|\\\\}}x.c","StartLine":6},{"Column":0,"Discriminator":0,"FileName":"/tmp{{/|\\\\}}x.c","FunctionName":"main","Line":14,"StartAddress":"0x400540","StartFileName":"/tmp{{/|\\\\}}x.c","StartLine":12}]} ## Invalid argument after a valid one. -# INLINE-NEXT:{"Error":{"Message":"unable to parse arguments: some text2"},"ModuleName":"{{.*}}/Inputs/addr.exe"} +# INLINE-NEXT:{"ModuleName":"{{.*}}/Inputs/addr.exe","Symbol":[{"Column":0,"Discriminator":0,"FileName":"","FunctionName":"","Line":0,"StartAddress":"","StartFileName":"","StartLine":0}]} ## Also check the last test case with llvm-adr2line. ## The expected result is the same with -f -i. # RUN: llvm-addr2line --output-style=JSON -f -i -e %p/Inputs/addr.exe < %p/Inputs/addr.inp | \ # RUN: FileCheck %s --check-prefix=INLINE-A2L --strict-whitespace --match-full-lines --implicit-check-not={{.}} ## Invalid first argument before any valid one. -# INLINE-A2L:{"Error":{"Message":"unable to parse arguments: some text"},"ModuleName":"{{.*}}/Inputs/addr.exe"} +# INLINE-A2L:{"ModuleName":"{{.*}}/Inputs/addr.exe","Symbol":[{"Column":0,"Discriminator":0,"FileName":"","FunctionName":"","Line":0,"StartAddress":"","StartFileName":"","StartLine":0}]} ## Resolve valid address. # INLINE-A2L-NEXT:{"Address":"0x40054d","ModuleName":"{{.*}}/Inputs/addr.exe","Symbol":[{"Column":3,"Discriminator":0,"FileName":"/tmp{{/|\\\\}}x.c","FunctionName":"inctwo","Line":3,"StartAddress":"0x400540","StartFileName":"/tmp{{/|\\\\}}x.c","StartLine":2},{"Column":0,"Discriminator":0,"FileName":"/tmp{{/|\\\\}}x.c","FunctionName":"inc","Line":7,"StartAddress":"0x400540","StartFileName":"/tmp{{/|\\\\}}x.c","StartLine":6},{"Column":0,"Discriminator":0,"FileName":"/tmp{{/|\\\\}}x.c","FunctionName":"main","Line":14,"StartAddress":"0x400540","StartFileName":"/tmp{{/|\\\\}}x.c","StartLine":12}]} ## Invalid argument after a valid one. -# INLINE-A2L-NEXT:{"Error":{"Message":"unable to parse arguments: some text2"},"ModuleName":"{{.*}}/Inputs/addr.exe"} +# INLINE-A2L-NEXT:{"ModuleName":"{{.*}}/Inputs/addr.exe","Symbol":[{"Column":0,"Discriminator":0,"FileName":"","FunctionName":"","Line":0,"StartAddress":"","StartFileName":"","StartLine":0}]} ## Note llvm-addr2line without -f does not print the function name in JSON too. # RUN: llvm-addr2line --output-style=JSON -i -e %p/Inputs/addr.exe < %p/Inputs/addr.inp | \ # RUN: FileCheck %s --check-prefix=NO-FUNC-A2L --strict-whitespace --match-full-lines --implicit-check-not={{.}} ## Invalid first argument before any valid one. -# NO-FUNC-A2L:{"Error":{"Message":"unable to parse arguments: some text"},"ModuleName":"{{.*}}/Inputs/addr.exe"} +# NO-FUNC-A2L:{"ModuleName":"{{.*}}/Inputs/addr.exe","Symbol":[{"Column":0,"Discriminator":0,"FileName":"","FunctionName":"","Line":0,"StartAddress":"","StartFileName":"","StartLine":0}]} ## Resolve valid address. # NO-FUNC-A2L-NEXT:{"Address":"0x40054d","ModuleName":"{{.*}}/Inputs/addr.exe","Symbol":[{"Column":3,"Discriminator":0,"FileName":"/tmp{{/|\\\\}}x.c","FunctionName":"","Line":3,"StartAddress":"0x400540","StartFileName":"/tmp{{/|\\\\}}x.c","StartLine":2},{"Column":0,"Discriminator":0,"FileName":"/tmp{{/|\\\\}}x.c","FunctionName":"","Line":7,"StartAddress":"0x400540","StartFileName":"/tmp{{/|\\\\}}x.c","StartLine":6},{"Column":0,"Discriminator":0,"FileName":"/tmp{{/|\\\\}}x.c","FunctionName":"","Line":14,"StartAddress":"0x400540","StartFileName":"/tmp{{/|\\\\}}x.c","StartLine":12}]} ## Invalid argument after a valid one. -# NO-FUNC-A2L-NEXT:{"Error":{"Message":"unable to parse arguments: some text2"},"ModuleName":"{{.*}}/Inputs/addr.exe"} +# NO-FUNC-A2L-NEXT:{"ModuleName":"{{.*}}/Inputs/addr.exe","Symbol":[{"Column":0,"Discriminator":0,"FileName":"","FunctionName":"","Line":0,"StartAddress":"","StartFileName":"","StartLine":0}]} diff --git a/llvm/test/tools/llvm-symbolizer/output-style-json-data.test b/llvm/test/tools/llvm-symbolizer/output-style-json-data.test --- a/llvm/test/tools/llvm-symbolizer/output-style-json-data.test +++ b/llvm/test/tools/llvm-symbolizer/output-style-json-data.test @@ -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 diff --git a/llvm/test/tools/llvm-symbolizer/output-style-json-frame.ll b/llvm/test/tools/llvm-symbolizer/output-style-json-frame.ll --- a/llvm/test/tools/llvm-symbolizer/output-style-json-frame.ll +++ b/llvm/test/tools/llvm-symbolizer/output-style-json-frame.ll @@ -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 diff --git a/llvm/test/tools/llvm-symbolizer/sym-verbose.test b/llvm/test/tools/llvm-symbolizer/sym-verbose.test --- a/llvm/test/tools/llvm-symbolizer/sym-verbose.test +++ b/llvm/test/tools/llvm-symbolizer/sym-verbose.test @@ -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 @@ -118,4 +118,4 @@ CHECK-NEXT: Column: 0 CHECK-NEXT: Discriminator: 2 -CHECK: some more text +CHECK: ?? diff --git a/llvm/test/tools/llvm-symbolizer/sym.test b/llvm/test/tools/llvm-symbolizer/sym.test --- a/llvm/test/tools/llvm-symbolizer/sym.test +++ b/llvm/test/tools/llvm-symbolizer/sym.test @@ -21,7 +21,8 @@ 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 @@ -30,7 +31,8 @@ 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 @@ -43,11 +45,11 @@ # 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 @@ -62,7 +64,7 @@ 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{{$}} @@ -70,9 +72,10 @@ 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{{$}} @@ -80,4 +83,4 @@ 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 diff --git a/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp b/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp --- a/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp +++ b/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp @@ -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" @@ -80,10 +81,10 @@ 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()) - errs() << "'" << Path << "': "; + if (!AuxInfo.empty()) + errs() << "'" << AuxInfo << "': "; EI.log(errs()); errs() << '\n'; } @@ -151,10 +152,14 @@ 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(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; @@ -174,34 +179,40 @@ 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; } @@ -209,20 +220,28 @@ // 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 @@ -268,6 +287,11 @@ 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, @@ -277,9 +301,16 @@ 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);