Skip to content

[lldb] Add support for evaluating expressions in anonymous namespaces. #96963

@mvanotti

Description

@mvanotti

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions