-
Notifications
You must be signed in to change notification settings - Fork 14.6k
Closed
Labels
Description
I have found that referring to variables inside anonymous namespaces in lldb
is a bit tricky if not impossible. In particular, if a variable is in an anonymous namespace, it will not be possible to create a breakpoint condition using the variable name as reported by lldb
.
See the following example:
#include <stddef.h>
#include <stdio.h>
namespace {
size_t AnonymousVar = 1;
namespace xoo {
size_t AnonymousXooVar = 2;
} // namespace xoo
} // namespace
namespace foo {
namespace {
size_t FooAnonymousVar = 3;
} // namespace
} // namespace foo
int main(int argc, char** argv) {
__asm__ volatile("nop");
printf("Hello World! Argc is %d, I'm %s\n", argc, argv[0]);
if (argc == 10000) {
// Use all variables to avoid them being optimized out.
__asm__ volatile("" ::"r"(AnonymousVar));
__asm__ volatile("" ::"r"(xoo::AnonymousXooVar));
__asm__ volatile("" ::"r"(foo::FooAnonymousVar));
}
}
Compiled with:
$ clang++ -Wall -Wextra -pedantic -std=c++17 -glldb -O0 repro.cc -o repro
And ran with the following python program:
#!/usr/bin/python3
import lldb
import os
def main():
debugger = lldb.SBDebugger.Create()
debugger.SetAsync(False)
target_prog = './repro'
argv = []
target = debugger.CreateTargetWithFileAndArch(
target_prog, lldb.LLDB_ARCH_DEFAULT_64BIT
)
assert target.IsValid()
bp = target.BreakpointCreateByName('main')
error = lldb.SBError()
process = target.Launch(
debugger.GetListener(),
argv,
[], # envp
None,
None,
None,
os.getcwd(),
0, # launch_flags
False, # stop_at_entry
error,
)
assert error.Success(), error
assert process.GetState() == lldb.eStateStopped
assert len(process.threads) == 1
thread = process.threads[0]
assert thread.IsValid()
frames = thread.get_thread_frames()
assert frames
frame = frames[0]
assert frame.IsValid()
print(frame.line_entry.line)
bp = target.BreakpointCreateByLocation('repro.cc', 26)
variable_names = set(['FooAnonymousVar', 'AnonymousVar', 'AnonymousXooVar'])
variables = [var for var in frame.variables if any([name in var.name for name in variable_names])]
condition = ' && '.join(f'{var.name} == {var.value}' for var in variables)
print(f'Setting Breakpoint condition to: {condition}')
bp.SetCondition(condition)
thread.StepOutOfFrame(frame)
assert(process.GetState() == lldb.eStateStopped)
assert frame.line_entry.line == 26, frame.line_entry.line
if __name__ == '__main__':
main()
$ python3 ./debug.py
24
Setting Breakpoint condition to: foo::(anonymous namespace)::FooAnonymousVar == 3 && (anonymous namespace)::AnonymousVar == 1 && (anonymous namespace)::xoo::AnonymousXooVar == 2
error: stopped due to an error evaluating condition of breakpoint 2.1: "foo::(anonymous namespace)::FooAnonymousVar == 3 && (anonymous namespace)::AnonymousVar == 1 && (anonymous namespace)::xoo::AnonymousXooVar == 2"
Couldn't parse conditional expression:
error: <user expression 0>:1:6: expected unqualified-id
foo::(anonymous namespace)::FooAnonymousVar == 3 && (anonymous namespace)::AnonymousVar == 1 && (anonymous namespace)::xoo::AnonymousXooVar == 2
^
error: <user expression 0>:1:7: use of undeclared identifier 'anonymous'
foo::(anonymous namespace)::FooAnonymousVar == 3 && (anonymous namespace)::AnonymousVar == 1 && (anonymous namespace)::xoo::AnonymousXooVar == 2
^
Note that this also happens when using lldb
from the cli, and you can sometimes omit the anonymous namespace, but it becomes trickier when you have more complex setups like foo::(anonymous namespace)::bar
and variables whose name collide.