Open ikey4u opened 2 months ago
@llvm/issue-subscribers-lldb
Author: zhq (ikey4u)
This is a great report thankyou!
I am just back from vacation, so it will be a few days until I can look into this.
Something I noticed on my Ubuntu system:
$ cat /tmp/test.c
#include <stdio.h>
void print_no_newline() {
printf("Output");
fflush(stdout);
}
void print_with_newline() {
printf("Output\n");
}
int main() {
return 0;
}
(lldb) p print_with_newline()
Output
(lldb) p print_no_newline()
(lldb) lldb)
<...then as you start to type the new characters overwrite...>
(lldb) p db)
<...>
(lldb) p print_with_newline()
Output
(lldb)
This is despite the program itself calling fflush
.
If I print a lot more characters I do see some of it:
(lldb) p print_no_newline()
(lldb) -----------------------------------------------------(lldb)
^---The cursor is here though.
So I think the data is making it out and to lldb but perhaps we don't reset the terminal correctly after printing it.
Note that the last (lldb) which is not right, and the content between the two (lldb) will not appear correctly on the terminal which seems is eat out by the terminal.
I wonder if the cursor is also after the first (lldb)
in this case too?
Something I noticed on my Ubuntu system:
$ cat /tmp/test.c #include <stdio.h> void print_no_newline() { printf("Output"); fflush(stdout); } void print_with_newline() { printf("Output\n"); } int main() { return 0; }
(lldb) p print_with_newline() Output (lldb) p print_no_newline() (lldb) lldb) <...then as you start to type the new characters overwrite...> (lldb) p db) <...> (lldb) p print_with_newline() Output (lldb)
This is despite the program itself calling
fflush
.If I print a lot more characters I do see some of it:
(lldb) p print_no_newline() (lldb) -----------------------------------------------------(lldb) ^---The cursor is here though.
So I think the data is making it out and to lldb but perhaps we don't reset the terminal correctly after printing it.
Note that the last (lldb) which is not right, and the content between the two (lldb) will not appear correctly on the terminal which seems is eat out by the terminal.
I wonder if the cursor is also after the first
(lldb)
in this case too?
Yes! And when you type directly, it will override the line (only the part walked by cursor), the remaining part of the line is still there.
I've reproduced your specific example on Ubuntu as well:
$ ./bin/lldb -- ./bin/clang++ --analyze -Xanalyzer -analyzer-checker=core.DivideZero ./divzero.cpp
<...>
(lldb) breakpoint set --name ExprEngine::Visit
Breakpoint 1: no locations (pending).
WARNING: Unable to resolve breakpoint to any actual locations.
(lldb) r
Process 3026646 launched: '/home/david.spickett/build-llvm-aarch64/bin/clang++' (aarch64)
1 location added to breakpoint 1
Process 3026646 stopped
<...>
-> 1722 PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(),
<...>
(lldb) p Pred->Location->dump()
"kind": "Statement", "stmt_kind": "DeclRefExpr", "stmt_id": 1512, "pointer": "0xaaaaaad030e0", "pretty": "x", "location": { "line": 2, "column": 14, "file": "./divzero.cpp" }, "stmt_point_kind": "PreStmtPurgeDeadSymbols" Evaluated this expression after applying Fix-It(s):
Pred->Location.dump()
(lldb) p Pred->Location.dump()
"kind": "Statement", "stmt_kind": "DeclRefExpr", "stmt_id": 1512, "pointer": "0xaaaaaad030e0", "pretty": "x", "location": { "line": 2, "column": 14, "file": "./divzero.cpp" }, "stmt_point_ki(lldb) reStmtPurgeDeadSymbols"(lldb)
^--Cursor is here.
A standalone reproducer for this is:
$ cat /tmp/test.c
#include <stdio.h>
typedef struct Foo {
void (*fn)(void);
} Foo;
void print_with_no_newline() {
fprintf(stderr, "abcdefghijklmnopqrstuvwxyz");
}
Foo f;
int main() {
Foo* the_foo = &f;
the_foo->fn = print_with_no_newline;
return 0;
}
Note that it prints to stderr like the dump does, stderr being unbuffered.
$ gcc /tmp/test.c -o /tmp/test.o -g && /tmp/test.o
$ ./bin/lldb /tmp/test.o -o "b main" -o "run"
(lldb) target create "/tmp/test.o"
Current executable set to '/tmp/test.o' (aarch64).
(lldb) b mainly indexing DWARF: test.o...
Breakpoint 1: where = test.o`main + 4 at test.c:14:8, address = 0x00000000000007d0
(lldb) run
<...>
-> 14 Foo* the_foo = &f;
15 the_foo->fn = print_with_no_newline;
<...>
(lldb) n
<...>
(lldb) n
<...>
-> 17 return 0;
18 }
(lldb) p the_foo.fn()
abcdefghijklmnopqrstuvwxyz Evaluated this expression after applying Fix-It(s):
the_foo->fn()
(lldb) p the_foo->fn()
(lldb) hijklmnopqrstuvwxyz(lldb)
^--Cursor is here
The interesting thing is that the "Evaluated this expression..." does seem to take care to appear after the debugee's output (though it'd be nice to be on a new line), which implies there's a way for the other output to be handled properly.
But anyway, I will see how that works. In the meantime your workaround is to add the newline manually:
(lldb) p the_foo.fn(); (void)fprintf(stderr, "\n")
abcdefghijklmnopqrstuvwxyz
Evaluated this expression after applying Fix-It(s):
the_foo->fn(); (void)fprintf(stderr, "\n")
(lldb) p the_foo->fn(); (void)fprintf(stderr, "\n")
abcdefghijklmnopqrstuvwxyz
(lldb)
I suspect that Location.dump()
does not add a newline because it calls print json that doesn't want to add a newline. I don't think anyone would object to you adding a newline in Location.dump()
though, if you feel like fixing it.
There are many dump functions in Clang Static Analyzer, add new line in dump()
is not a good solution since it just avoids the problem in this situation . Have a quick search in source of lldb, I got the following code snippet:
// Only mention Fix-Its if the expression evaluator applied them.
// Compiler errors refer to the final expression after applying Fix-It(s).
if (!fixed_expression.empty() && target.GetEnableNotifyAboutFixIts()) {
Stream &error_stream = result.GetErrorStream();
error_stream << " Evaluated this expression after applying Fix-It(s):\n";
error_stream << " " << fixed_expression << "\n";
}
// TODO
I guess that \n
flushes the stream.
Add following line to the location marked as // TODO
solves the problem:
result.GetOutputStream().EOL();
But this is not great, one possible elegant solution is to check if the previous output/error stream ends with a newline, if not we force append a new line to the stream.
When debugging clang static analyzer, the output of evaluating dump of ProgramState of ExplodedNode seems does not work in LLDB, the issue is weirded that I cannot describe it in simple one or two statements. As a result, I spend a lot of time to create a docker environment that easy you to reproduce the problem, and the problem is described in the last.
Host
The following operations are executed in host.
Create docker base image
Create docker container
Docker
The following operations are executed in docker, you can use following command to get into docker on your host:
Update yum
Install dependencies
Build lldb
The time has finally come to reproduce the bug
Create a div zero file in home directory:
Load it using lldb:
Disable ASLR in lldb when using lldb in a docker to avoid error
personality set failed: Operation not permitted
:And set a breakpoint in lldb:
Then run lldb:
When the breakpoint hits, checke the ExplodeNode state using following command:
Note that lldb auto fix the command, and let's type the right command, the bug appears:
Note that the last
(lldb)
which is not right, and the content between the two(lldb)
will not appear correctly on the terminal which seems is eat out by the terminal.