[libcxx-commits] [libcxx] WIP - [libc++][debugging] P2546R5: Debugging support & P2810R4: `is_debugger_present` `is_replaceable` (PR #81447)

Adrian Vogelsgesang via libcxx-commits libcxx-commits at lists.llvm.org
Wed Apr 24 19:14:21 PDT 2024


vogelsgesang wrote:

Personally, I would have used a different approach to implement this.
Not sure if this makes sense.
I would appreciate your thoughts on it 🙂 

## Proposed Alternative Design

In libc++, I would have implemented this as

```cpp
namespace std {

bool __is_debugger_present_hook = false;
inline bool is_debugger_present() noexcept { return __is_debugger_present_hook; }

__attribute__((noinline)) inline void breakpoint_if_debugging() noexcept {}

__attribute__((noinline)) inline void breakpoint() noexcept {
  if (!is_debugger_present()) __force_breakpoint();
}

}
```

and would then do the actual work inside lldb / gdb / _your favorite debugger_.

### breakpoint_if_debugging

If the function exists, lldb / gdb would automatically set a breakpoint in this function. The mechanism for this already exists in both gdb and lldb, which are already doing something similar for `__jit_debug_register_code` (which is used as part of the [debugger support for jitted code](https://sourceware.org/gdb/current/onlinedocs/gdb.html/JIT-Interface.html#JIT-Interface)). See [this source code](https://github.com/llvm/llvm-project/blob/d5308949cf884d8e4b971d51a8b4f73584c4adec/lldb/source/Plugins/JITLoader/GDB/JITLoaderGDB.cpp#L184) for lldb's implementation of this.

The function must be marked as no-inline, such that the function isn't just optimized out and the debugger can still find the function to set the breakpoint.

Benefits:

* Better user experience: The debugger would stop top-level, directly in the `breakpoint_if_debugging` function and not inside the internal `__breakpoint` function.
* Performance: As long as no debugger is attached, this will be a simple call to an empty, non-inlined function. More efficient  than reading `/proc/self/status` on every call.
* Does not crash under `perf` (and similar tools): Afaict, the current implementation of `breakpoint_if_debugging` would not work for programs running under `perf`: `is_debugger_present` would return true, and hence we would call `__breakpoint` which calls `__builtin_debugtrap` which in turn lowers to an `int3` on X86. However, `perf` is not actually a debugger and hence it is not prepared to handle a debugging interrupt signal.
* More portable: It will automatically support all platforms to which lldb / gdb is ported

Unclear: would this also work for stripped binaries / binaries without debug symbols? Is there maybe some trick to mark this as an exported, non-strippable symbol such that the symbol name also exists in binaries without debug info?

### is_debugger_present

Upon attaching to a process, lldb / gdb would check if a boolean by the name `std::__is_debugger_present_hook` exists. If so, set it to true. Upon detaching, it would reset the value to false / its previous value.

Benefits:
* Performance: The `is_debugger_present` function can be inlined and optimized down to a read of a boolean variable. This is more efficient than the current implementation which cannot be inlined and does system calls.
* More accurate: The current implementation also reports `perf` and similar performance tracing tools as a debugger. `perf` is not a debugger, though, and in case I am running under `perf`, I usually don't want to spend the extra CPU cycles to execute additional code / show debug UIs etc. (see [motivation of is_debugger_present](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2546r5.html#_is_debugger_present))
* More portable: It doesn't depend on the file system (`/proc/self/status` and `_LIBCPP_HAS_NO_FILESYSTEM`, `_LIBCPP_HAS_NO_LOCALIZATION`)
* More portable: It will automatically support all platforms to which lldb / gdb / <your favorite debugger> is ported

### breakpoint

For `breakpoint()` I would also let the debugger set a breakpoint, just as for `breakpoint_if_debugging`.

However, in case no debugger is attached, we still want to break. Hence, we call `__breakpoint` with the same implementation as in your pull request.

## Usage with unaware debuggers and older lldb / gdb versions

Even with a debugger that is not aware of this special "C++ debugging protocol", I can still manually set the breakpoint in `std::breakpoint_if_debugging` and flip `is_debugger_present` from within the gdb / lldb command line, just as I can set a breakpoint anywhere else. I guess, I could even add this to my startup configuration file for lldb.

## Implementation effort

Inside libc++, this alternative would be certainly less code. 

In lldb / gdb, this would require some special handling of those debugging functions. But for a good user experience, dedicated lldb support might be necessary, anyway, as previously noted in:

> debugger support: when the debugger breaks in breakpoint() it breaks inside the implementaiton, which isn't an optimal user experience. Perhaps LLDB (the debugger) can be thought to break on breakpoint() call site, similar to the /JMC option in MSVC but this is unrelated work to this patch.

## [P2810R4: is_debugger_present is_replaceable](https://isocpp.org/files/papers/P2810R4.html)

This paper unfortunately gets in the way of the proposed alternative implementation, because after it disallows inlining `is_debugger_present` afaict. This is a bit unfortunate, since I think that the `__is_debugger_present_hook` approach would still reach the goals of P2810R4. A user could, e.g., use `std::__is_debugger_present_hook = true` to "Wire it to return true in special verifying builds of their application" or could set it to true "in response to a key press or other external mechanical signal" (see motivation of [P2810R4](https://isocpp.org/files/papers/P2810R4.html#_motivation)).

Nevertheless, even with P2810R4, the overall approach would still work. We would only lose the small performance benefit from inlining `is_debugger_present`.

https://github.com/llvm/llvm-project/pull/81447


More information about the libcxx-commits mailing list