Issue 96401
Summary [lldb] `finish` doesn't work well with sanitizer coverage and if-else-chains.
Labels new issue
Assignees
Reporter mvanotti
    I'm trying to run the debugger on a program that was compiled with `-fsanitize-coverage=bb,no-prune,trace-pc-guard`, and I want to step-out of all the calls to `__sanitizer_cov_trace_pc_guard`.

I've found that when I attempt to do a `finish` in the `else` case of an `if` with an `else if` clause, lldb gets confused.

See the following example:

```c++
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/random.h>

[[clang::noinline]] void foo(void) {
  uint64_t random_num = 0;
  ssize_t res = getrandom(&random_num, sizeof(random_num), 0);
  assert(res != -1);
 assert(static_cast<size_t>(res) == sizeof(random_num));

  if (random_num == UINT64_MAX) {
    __asm__ volatile("nop");
  } else if (random_num == 0) {
    __asm__ volatile("nop");
  } else if (random_num == 1) {
    __asm__ volatile("nop");
  } else {
 __asm__ volatile("pause");
  }
}

int main(void) {
 foo();
  fprintf(stderr,
          "If we are single-stepping, we should have a stop here on main.\n");
  return 0;
}

extern "C" [[clang::noinline]] void __sanitizer_cov_trace_pc_guard(uint32_t *) {
  // Do (almost) nothing!
  __asm__ volatile("pause");
}

extern "C" void __sanitizer_cov_trace_pc_guard_init(uint32_t *, uint32_t *) {}
```

Compiled with:

```
$ clang++ -Wall -Wextra -pedantic -std=c++17 -gdwarf-5 -O0 -fsanitize-coverage=bb,no-prune,trace-pc-guard    program.cc   -o program
```

Running it under lldb, single stepping, and calling `finish` each time we are in `__sanitizer_cov_trace_pc_guard`:

```
(lldb) target create "./program"
Current executable set to '/home/user/lldb-test/program' (x86_64).
(lldb) breakpoint set --file program.cc --line 21
Breakpoint 1: where = program`foo() + 369 at program.cc:21:5, address = 0x000000000002e631
(lldb) run
Process 3612485 launched: '/home/user/lldb-test/program' (x86_64)
Process 3612485 stopped
* thread #1, name = 'program', stop reason = breakpoint 1.1
    frame #0: 0x0000555555582631 program`foo() at program.cc:21:5
   18     } else if (random_num == 1) {
   19       __asm__ volatile("nop");
   20     } else {
-> 21       __asm__ volatile("pause");
   22     }
   23 }
   24  
(lldb) s
Process 3612485 stopped
* thread #1, name = 'program', stop reason = step in
    frame #0: 0x00005555555824a8 program`__sanitizer_cov_trace_pc_guard((null)=0x0000555555599b50) at program.cc:33:3
   30   }
   31  
   32   extern "C" [[clang::noinline]] void __sanitizer_cov_trace_pc_guard(uint32_t *) {
-> 33     // Do (almost) nothing!
   34     __asm__ volatile("pause");
 35   }
   36  
(lldb) finish
Process 3612485 stopped
* thread #1, name = 'program', stop reason = step out
    frame #0: 0x0000555555582641 program`foo() at program.cc:21:5
   18     } else if (random_num == 1) {
   19       __asm__ volatile("nop");
   20     } else {
-> 21       __asm__ volatile("pause");
   22     }
   23 }
   24  
(lldb) s
Process 3612485 stopped
* thread #1, name = 'program', stop reason = step in
    frame #0: 0x00005555555824a8 program`__sanitizer_cov_trace_pc_guard((null)=0x0000555555599b54) at program.cc:33:3
   30   }
   31  
   32   extern "C" [[clang::noinline]] void __sanitizer_cov_trace_pc_guard(uint32_t *) {
-> 33     // Do (almost) nothing!
   34     __asm__ volatile("pause");
 35   }
   36  
(lldb) finish
If we are single-stepping, we should have a stop here on main.
Process 3612485 exited with status = 0 (0x00000000) 
(lldb) 
```

The outcome is less than ideal: instead of executing until the end of `__sanitizer_cov_trace_pc_guard`, the program executes until the end of the program.

Here is a python script that replicates this behavior (this is how I initially observed the issue):

```python
#!/usr/bin/python3
import lldb
import os

def main():
  debugger = lldb.SBDebugger.Create()
 debugger.SetAsync(False)

  target_prog = './program'
  argv = []
  envp = []

  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
  traced_functions = set(['main', 'foo()'])

  while 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()

    func_name = frame.function.name
    if func_name not in traced_functions:
 print(f'Stepping out of function {func_name}')
 thread.StepOutOfFrame(frame)
      continue
    print(f'Func {func_name:>10} | Location {os.path.basename(str(frame.line_entry.file)):>10}:{frame.line_entry.line:>3} | pc @ {hex(frame.pc)}')

    thread.StepInto()

if __name__ == '__main__':
  main()
```

Output:

```
Func       main | Location program.cc: 25 | pc @ 0x555555582688
Stepping out of function ::__sanitizer_cov_trace_pc_guard(uint32_t *)
Func       main | Location program.cc: 25 | pc @ 0x555555582694
Func       main | Location program.cc: 26 | pc @ 0x55555558269b
Func      foo() | Location program.cc:  8 | pc @ 0x5555555824c8
Stepping out of function ::__sanitizer_cov_trace_pc_guard(uint32_t *)
Func      foo() | Location program.cc:  9 | pc @ 0x5555555824d4
Func      foo() | Location program.cc: 10 | pc @ 0x5555555824dc
Stepping out of function __GI___getrandom
Func      foo() | Location program.cc: 10 | pc @ 0x5555555824ec
Func      foo() | Location program.cc: 11 | pc @ 0x5555555824f0
Stepping out of function ::__sanitizer_cov_trace_pc_guard(uint32_t *)
Func      foo() | Location program.cc: 11 | pc @ 0x55555558250b
Func      foo() | Location program.cc: 12 | pc @ 0x55555558253f
Stepping out of function ::__sanitizer_cov_trace_pc_guard(uint32_t *)
Func      foo() | Location program.cc: 12 | pc @ 0x55555558254f
Stepping out of function ::__sanitizer_cov_trace_pc_guard(uint32_t *)
Func      foo() | Location program.cc: 12 | pc @ 0x55555558256a
Func      foo() | Location program.cc: 14 | pc @ 0x55555558259e
Stepping out of function ::__sanitizer_cov_trace_pc_guard(uint32_t *)
Func      foo() | Location program.cc: 14 | pc @ 0x5555555825ae
Func      foo() | Location program.cc: 16 | pc @ 0x5555555825cf
Stepping out of function ::__sanitizer_cov_trace_pc_guard(uint32_t *)
Func      foo() | Location program.cc: 16 | pc @ 0x5555555825df
Func      foo() | Location program.cc: 18 | pc @ 0x555555582600
Stepping out of function ::__sanitizer_cov_trace_pc_guard(uint32_t *)
Func      foo() | Location program.cc: 18 | pc @ 0x555555582610
Func      foo() | Location program.cc: 21 | pc @ 0x555555582631
Stepping out of function ::__sanitizer_cov_trace_pc_guard(uint32_t *)
Func      foo() | Location program.cc: 21 | pc @ 0x555555582641
Stepping out of function ::__sanitizer_cov_trace_pc_guard(uint32_t *)
```

I haven't tried this with the `lldb` version at trunk yet, will report back once I do.

_______________________________________________
llvm-bugs mailing list
llvm-bugs@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs

Reply via email to