This is an archive of the discontinued LLVM Phabricator instance.

[symbolizer] Change reaction on invalid input
ClosedPublic

Authored by sepavloff on Aug 6 2023, 4:05 AM.

Details

Summary

If llvm-symbolizer finds a malformed command, it echoes it to the
standard output. New versions of binutils (starting from 2.39) allow to
specify an address by a symbols. Implementation of this feature in
llvm-symbolizer makes the current reaction on invalid input
inappropriate. Almost any invalid command may be treated as a symbol
name, so the right reaction should be "symbol not found" in such case.

The exception are commands that are recognized but have incorrect
syntax, like "FILE:FILE:". The utility must produce descriptive
diagnostic for such input and route it to the stderr.

This change implements the new reaction on invalid input and is a
prerequisite for implementation of symbol lookup in llvm-symbolizer.

Diff Detail

Event Timeline

sepavloff created this revision.Aug 6 2023, 4:05 AM
Herald added a project: Restricted Project. · View Herald TranscriptAug 6 2023, 4:05 AM
Herald added a subscriber: hiraditya. · View Herald Transcript
sepavloff requested review of this revision.Aug 6 2023, 4:05 AM
Herald added a project: Restricted Project. · View Herald TranscriptAug 6 2023, 4:05 AM
sepavloff updated this revision to Diff 547586.Aug 6 2023, 8:01 AM

Update patch

LGTM. Returning an explanatory Error instead of just a boolean status seems like a big improvement to me.

ikudrin accepted this revision.Aug 8 2023, 5:54 PM
This revision is now accepted and ready to land.Aug 8 2023, 5:54 PM
MaskRay accepted this revision.Aug 8 2023, 6:46 PM
jhenderson requested changes to this revision.Aug 8 2023, 11:52 PM

Generally looks fine, but there are a number of small issues, and I'm not convinced by the JSON output behaviour change, hence requesting changes to clear the "accepted" state.

llvm/docs/CommandGuide/llvm-symbolizer.rst
21–22
llvm/docs/ReleaseNotes.rst
165

Nit: I don't think you want 2 blank lines (1 will do).

llvm/test/tools/llvm-symbolizer/file-prefix.test
8

Rather than redirecting stdin, use the --input-file option.

(Motivation for me is that the shell I commonly use, Windows PowerShell, doesn't support < for stdin redirection)

llvm/test/tools/llvm-symbolizer/invalid-input-address.test
2
26

Nit: missing new line at EOF.

llvm/test/tools/llvm-symbolizer/options-from-env.test
1–3

Let's use LLVM and GNU prefixes rather than SYMB and CHECK (corresponds to LLVM and GNU styles).

llvm/test/tools/llvm-symbolizer/output-style-json-code.test
28

These seem like a regression to me in the JSON output, as it's no longer recording the error?

llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp
155–162

createStringError or makeStringError would be better than getStringError in my opinion - get implies to me that the Error exists and that you're just fetching it from somewhere.

182

Nit: here and below, error messages should use lower case for the first letter. See https://llvm.org/docs/CodingStandards.html#error-and-warning-messages.

238

Rather than just producing "invalid argument", could we give additional information about what the argument is that is deemed to be invalid, e.g. "1foo is not a valid address" or something like that?

This revision now requires changes to proceed.Aug 8 2023, 11:52 PM
sepavloff updated this revision to Diff 548677.Aug 9 2023, 10:38 AM

Rebase. Address reviewers' notes

sepavloff marked 8 inline comments as done.Aug 9 2023, 11:47 AM
sepavloff added inline comments.
llvm/test/tools/llvm-symbolizer/output-style-json-code.test
28

It is not an error anymore, it is "no source information". If llvm-symbolizer is called with an address without such information, for example:

llvm-symbolizer -e llvm-symbolizer --output-style=JSON 0x3

the output is similar:

[{"Address":"0x3","ModuleName":"llvm-symbolizer","Symbol":[{"Column":0,"Discriminator":0,"FileName":"","FunctionName":"","Line":0,"StartAddress":"","StartFileName":"","StartLine":0}]}]

It is consistent with the new behavior for other output styles, and is natural if symbol lookup is implemented.

llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp
238

Actually it is not an error, llvm-symbolize processes it as an address which has no related source information. It is consistent with the present behavior of GNU addr2line (even of version before 2.39). An error is returned so that the message "??:0" could be printed immediately. To distinguish this case from the errors that require emitting a diagnostic message, a different error type is used here. Actually particular error code is not used, only different class matters. The code in symbolizeInput can react differently in such case.

Implementation of symbol lookup will make this trick unnecessary, as any string could potentially be a symbol name.

jhenderson accepted this revision.Aug 9 2023, 11:55 PM

Thanks for addressing my concerns. LGTM.

llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp
238

Okay, on the basis that this is only temporary, this is fine. The comment is useful too.

This revision is now accepted and ready to land.Aug 9 2023, 11:55 PM
MaskRay accepted this revision.Aug 11 2023, 8:00 AM
MaskRay added inline comments.
llvm/include/llvm/DebugInfo/Symbolize/DIPrinter.h
36

remove this change.

https://en.cppreference.com/w/cpp/utility/optional/optional
"1) Constructs an object that does not contain a value."

MaskRay added inline comments.Aug 11 2023, 8:02 AM
llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp
211

cannot be empty seems better than is incorrect

This revision was landed with ongoing or failed builds.Aug 12 2023, 1:58 AM
This revision was automatically updated to reflect the committed changes.
sepavloff added inline comments.Aug 12 2023, 2:04 AM
llvm/include/llvm/DebugInfo/Symbolize/DIPrinter.h
36

The initializer was added because of the warning^

export/serge/llvm/space2/llvm-project/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp:292:43: warning: missing field 'Address' initializer [-Wmissing-field-initializers]
          Request SymRequest = {ModuleName};

I added the second item to the initializer and removed the initializer in the struct declaration.

llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp
211

It is not for missing argument but for the case of unterminated strings:

$ cat sym.inp
FILE:"addr.exe 0x400540
$ ./llvm-symbolizer <sym.inp
$ ./llvm-symbolizer: error: 'FILE:"addr.exe 0x400540': input file name is incorrect
dyung added a subscriber: dyung.Aug 12 2023, 4:09 PM

Hi @sepavloff, I'm seeing some weird behavior on our Windows build bot after your change (https://lab.llvm.org/buildbot/#/builders/216/builds/25497).

When building your change, it seems one of the tests is hanging, and it keeps a lock on llvm-symbolizer.exe which then causes subsequent builds to fail as they cannot write to the file. The only way to recover the build bot that I've found is to login, kill the zombie llvm-symbolizer process and then I can delete the binary so that it can be re-written on a later build. Any idea why this might be happening?

From process explorer, this is the command that is getting stuck:

"c:\program files\python310\python.exe" Z:\b\llvm-clang-x86_64-sie-win\llvm-project\llvm\test\Support\interrupts.test symbolizer z:\b\llvm-clang-x86_64-sie-win\build\bin\llvm-symbolizer.exe

I'm also seeing a similar issue affecting another Windows build bot https://lab.llvm.org/buildbot/#/builders/65/builds/10919. Something about your change does not seem to sit well with Windows builders. Can you take a look or revert if you need time to investigate? I may revert it soon since it requires manual intervention to get the build bot back running again, but wanted to check and see if you had any idea what might be happening first.

dyung added a comment.Aug 12 2023, 7:46 PM

Sorry, I had to revert your change to get the Windows build bots back up and running.

To anyone here who is trying to recover your build bot, I found that killing the llvm-symbolizer.exe process in task manager and then restarting the build after my revert fixes the issue.

Hi @dyung. Thank you for recovering builds and for the provided information. I saw the fails on some bots but had no information about what is wrong. Now I have a starting point.

sepavloff reopened this revision.Aug 13 2023, 9:22 AM

Reopen to run tests for the fixed patch.

This revision is now accepted and ready to land.Aug 13 2023, 9:22 AM
sepavloff updated this revision to Diff 549710.Aug 13 2023, 9:22 AM

Attempt to fix hanging test.

jhenderson requested changes to this revision.Aug 13 2023, 11:53 PM

As far as I can tell, the only "fix" you've made since the version that caused the hangs was to the test itself. That means that llvm-symbolizer could still hang under certain conditions surely, when it didn't used to...

If I squint hard enough, I could maybe see why this change is acceptable, but I'd like you to explain the change and why it isn't going to cause end-users hanging issues in practice.

This revision now requires changes to proceed.Aug 13 2023, 11:53 PM

The test hang is caused by the way llvm-symbolizer is ised in the test.

The test invokes llvm-symbolizer without arguments. So it tries to get both binary file an address from stdin. The script in the original test supplies foo to the llvm-symbolizer. This is an incorrect input, foo is treated as a binary file, but an address is absent, so the old llvm-symbolizer echoes foo on stdout. The script gets output on stdout and finishes.

The new llvm-symbolizer also detects the invalid command and puts descriptive diagnostic on stderr:

$ ./llvm-symbolizer
foo
./llvm-symbolizer: error: 'foo': no input filename is specified

Nothing appears on stdout in this case and the script continues waiting, - the test hangs.

Adding two words to the input of llvm-symbolizer make the command syntactically correct. Obviously, the tool cannot find source information in response to such command, so ??:0 is printed on stdout. The test script gets data it is wainting for and the test normally finishes.

Right, that's what I figured was the issue, but could you answer this part of my concern:

[explain] why it isn't going to cause end-users hanging issues in practice

My opinion is that if someone is in interactive mode and they get an error, it may make sense to exit llvm-symbolizer, rather than continue. How does this differ to the previous (prior to your patch) behaviour for other error cases (i.e. cases other than "missing an input file/address pair")?

My opinion is that if someone is in interactive mode and they get an error, it may make sense to exit llvm-symbolizer, rather than continue. How does this differ to the previous (prior to your patch) behaviour for other error cases (i.e. cases other than "missing an input file/address pair")?

This is behavior of llvm-symbolizer before this patch. Whenever it detects an invalid input it echoes the input and continue waiting on stdin. This behavior makes sense, - if the program got wrong command from interactive input, it can continue working because the next commands may do useful job.

Anyway, it is not llvm-symbolizer that caused the hang, it is blocking read in the test script, which waits input from the stream that is empty in the case of new llvm-symbolizer.

jhenderson added a comment.EditedAug 15 2023, 12:14 AM

My opinion is that if someone is in interactive mode and they get an error, it may make sense to exit llvm-symbolizer, rather than continue. How does this differ to the previous (prior to your patch) behaviour for other error cases (i.e. cases other than "missing an input file/address pair")?

This is behavior of llvm-symbolizer before this patch. Whenever it detects an invalid input it echoes the input and continue waiting on stdin. This behavior makes sense, - if the program got wrong command from interactive input, it can continue working because the next commands may do useful job.

Anyway, it is not llvm-symbolizer that caused the hang, it is blocking read in the test script, which waits input from the stream that is empty in the case of new llvm-symbolizer.

Sorry, I'm struggling to follow what you're saying. My concern is that because we had a test script that has gone from working to hanging, it's possible that an end user's script will be in the same state, so when they get the updated llvm-symbolizer, they will have a script that was previously working but is now hanging. I'm not concerned by llvm-symbolizer changing in other ways that could potentially invalidate their script, but introducing a potential path to a hang is a cause for concern. What does llvm-addr2line do in this sort of situation? Does it print something like "??:0:0" (as well as the error), because I think that might be a good answer.

@MaskRay/@ikudrin, any thoughts?

MaskRay accepted this revision.EditedAug 15 2023, 5:06 PM

My opinion is that if someone is in interactive mode and they get an error, it may make sense to exit llvm-symbolizer, rather than continue. How does this differ to the previous (prior to your patch) behaviour for other error cases (i.e. cases other than "missing an input file/address pair")?

This is behavior of llvm-symbolizer before this patch. Whenever it detects an invalid input it echoes the input and continue waiting on stdin. This behavior makes sense, - if the program got wrong command from interactive input, it can continue working because the next commands may do useful job.

Anyway, it is not llvm-symbolizer that caused the hang, it is blocking read in the test script, which waits input from the stream that is empty in the case of new llvm-symbolizer.

Sorry, I'm struggling to follow what you're saying. My concern is that because we had a test script that has gone from working to hanging, it's possible that an end user's script will be in the same state, so when they get the updated llvm-symbolizer, they will have a script that was previously working but is now hanging. I'm not concerned by llvm-symbolizer changing in other ways that could potentially invalidate their script, but introducing a potential path to a hang is a cause for concern. What does llvm-addr2line do in this sort of situation? Does it print something like "??:0:0" (as well as the error), because I think that might be a good answer.

@MaskRay/@ikudrin, any thoughts?

Is llvm/test/Support/interrupts.test the only failing test? (https://lab.llvm.org/buildbot/#/builders/216/builds/25497/steps/7/logs/stdio linked by @dyung above has tons of logs but I cannot find the relevant line.)

If yes, I think the latest revision contains the right change.

-     proc.stdin.write(b'foo\n')
+    proc.stdin.write(bytes(sys.argv[2] + " 0\n", 'ascii'))
% /tmp/Debug/bin/llvm-symbolizer
/tmp/Debug/bin/llvm-symbolizer 0
??
??:0:0

foo
/tmp/Debug/bin/llvm-symbolizer: error: 'foo': no input filename is specified

Since llvm-symbolizer no longer echoes (to stdout) the invalid foo input, proc.stdout.readline() in the Python script will block.
Switching to symbolizing llvm-symbolizer itself will give some stdout output.

jhenderson accepted this revision.Aug 22 2023, 11:57 PM

I took a break away from this for a few days, and decided that I shouldn't block this patch based on the behaviour change. However, I do think the specific case that can now hang (i.e. the case that caused the test to fail) should be highlighted in the release note, so that users can have a hint as to why their script started hanging after they updated to the latest llvm-symbolizer. LGTM with that.

llvm/docs/ReleaseNotes.rst
164

It might be worth noting explicitly the change in output behaviour for invalid behaviour here, namely that none is produced on stdout. Specifically, I want users to at least be aware that the llvm-symbolizer change could introduce a hang in scripts that were potentially poorly written ebfore.

llvm/test/tools/llvm-symbolizer/debuginfod.test
42
This revision is now accepted and ready to land.Aug 22 2023, 11:57 PM
MaskRay added inline comments.Aug 23 2023, 12:05 AM
llvm/test/tools/llvm-symbolizer/debuginfod.test
44

Delete llvm-symbolizer{{.*}}: . We generally don't test things before error:.

68

delete llvm-symbolizer{{.*}}: . ditto below

llvm/test/tools/llvm-symbolizer/file-prefix.test
9

delete llvm-symbolizer{{.*}}:

sepavloff updated this revision to Diff 552686.Aug 23 2023, 6:10 AM

Attempt to make llvm-symbolizer more hang-resistant

Behavior of llvm-symbolizer is slightly modified to avoid hanging.
If it receives data from the standard input, it always prints some
output on stdout. If the command is invalid, it prints ??:0 in
addition to the diagnostic on stderr. If a user makes blocking
read of llvm-symbolizer stdout, it won't wait forever with the
updated utility.

sepavloff marked 4 inline comments as done.Aug 23 2023, 6:12 AM
jhenderson added inline comments.Aug 23 2023, 11:43 PM
llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp
207–208
211

"is incorrect" doesn't seem right to me either. It doesn't tell the user how it is incorrect.

Also, I couldn't see a test case for this?

312

Does this really need to be conditional on interactive mode? It seems an unnecessary complication to me.

Enhance error messages a bit

sepavloff marked an inline comment as done.Aug 27 2023, 12:44 AM
sepavloff added inline comments.
llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp
211

Changed to more specific message about unbalanced quotes.

312

This is an unneeded complication, removed.

Just some nits left from me.

llvm/test/tools/llvm-symbolizer/get-input-file.test
5
9
29

This comment shoudl say something about unbalanced quotes, not "unterminated string", which implies something about a missing \0 to me.

48

Nit: no new line at EOF.

llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp
195
207–208

My apologies, this was stupid of me. It should be "input file has already been specified"

224

"is" -> "has been"

230

"is" -> "has been"

sepavloff updated this revision to Diff 554293.Aug 29 2023, 6:23 AM

Address reviewer's notes

sepavloff marked 8 inline comments as done.Aug 29 2023, 6:25 AM
jhenderson accepted this revision.Aug 29 2023, 7:05 AM

LGTM, with one remaining nit.

llvm/test/tools/llvm-symbolizer/get-input-file.test
5

Sorry, missed this typo.

MaskRay accepted this revision.Aug 29 2023, 3:17 PM
This revision was automatically updated to reflect the committed changes.