vadimcn / codelldb

A native debugger extension for VSCode based on LLDB
https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb
MIT License
2.54k stars 245 forks source link

some features don't work when stopped on exception breakpoints #1133

Open breathe opened 1 month ago

breathe commented 1 month ago

OS: macOS 14.6.1 (23G93) VSCode version:

Version: 1.92.2 (Universal)
Commit: fee1edb8d6d72a0ddff41e5f71a671c23ed924b9
Date: 2024-08-14T17:29:30.058Z (2 wks ago)
Electron: 30.1.2
ElectronBuildId: 9870757
Chromium: 124.0.6367.243
Node.js: 20.14.0
V8: 12.4.254.20-electron.0
OS: Darwin arm64 23.6.0

CodeLLDB version:v1.10.0 Compiler:

clang --version
Apple clang version 15.0.0 (clang-1500.3.9.4)
Target: arm64-apple-darwin23.6.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin

Debuggee: I'm debugging a c++ extension module for python. The module uses nanobind to integrate with the python api. The process entrypoint is the python interpreter. I attach lldb to the process just for debugging code defined within the c++ extension module.

Problem Overview

From what I've been able to tell there's no support for the equivalent of 'break on exception' in CodeLLDB. To work around that I've been adding to my .lldbinit to configure this behavior by adding for example breakpoint set -E c++ to my .lldbinit file.

These breakpoints get hit and pause execution in vscode as expected -- however I've noticed two issues whenever the program execution has been halted due to an exception.

Problem 1

First off -- the 'excluded callers' feature doesn't work when in this mode. I can't right-click on any of the elements of the callstack to ignore the breakpoint -- when I right click on a frame and choose 'Exclude caller' nothing happens -- nothing gets added to Excluded Callers and no errors are added to the LLDB output logs...

To work around that -- I've augmented my .lldbinit with a bit of python scripting -- so my lldb init looks like this

command script import ./lib_native/lldb_scripts/lldb_stop_on_cpp_exceptions.py

with lldb_stop_on_cpp_exceptions.py like this:

import lldb

def stop_on_exception(frame, bp_loc, dict):
    # Accessing the current thread's frame and iterate over all frames in the call stack
    thread = frame.GetThread()

    function_call_patterns_to_skip = [
        "osgeo::proj::io::AuthorityFactory::createCoordinateReferenceSystem"
    ]
    for f in thread:
        # Check if the exception stack contains any of the patterns we want to skip

        function_call = f.GetFunctionName()
        for skip_pattern in function_call_patterns_to_skip:
            if skip_pattern in function_call:
                print(
                    f"cpp exception raised -- debugger not breaking on it because call stack matched skip_pattern {skip_pattern} matched against frame: {f.GetFunctionName()}"
                )
                return False

    return True

def __lldb_init_module(debugger, dict):
    debugger.HandleCommand("breakpoint set -E c++")
    debugger.HandleCommand(
        "breakpoint command add -F lldb_stop_on_cpp_exceptions.stop_on_exception"
    )

This configures lldb to stop on cpp exceptions. Each time a cpp exception is thrown, lldb first runs the stop_on_exception script above to decide whether to pause on it. I then try to emulate the 'exclude callers' mechanism with the function_call_patterns_to_skip list that I can edit as I need. This appears to work in the sense that I don't stop on exceptions which have frames that match one of the skip patterns but do stop on exceptions that don't.

But it would be nicer if could somehow just configure this via the Excluded Callers mechanism.

Problem 2

Normally when I'm paused at a breakpoint in vscode, I can click on frames within the call stack and then examine variables accessible at that scope in the debug console.

When stopped at an exception breakpoint however something works incorrectly. I can click on a frame and then attempt to examine variables from that frame -- however the values printed always appear to be some manner of empty default (NaN for doubles, the empty vector for Eigen::Vector instances ...) -- I actually don't know how to characterize the way the values are wrong -- but local variables defined in functions are accessible for printing in the debug console when I click on the frame -- but then they print incorrectly ...

If I instead use lldb's frame select <frame_num> command within the debug console to navigate up the stack to the frame I want to examine -- then I can print things and they print out normally ...

Try and illustrate -- here's an image showing me paused at an exception and then clicking up the stack one frame

image

code-lldb takes me to the correct frame

image

In debug console I enter

?initial_guess
rows: 0, cols: 0, data_order: RowMajor

But that output doesn't make any sense because initial_guess was just initialized to be length 5 and all zeros and nothing changed it.

If I issue frame select 1 and then print again I see the correct/expected value ...

?initial_guess
rows: 5, cols: 1, data_order: RowMajor
[ 0;
  0;
  0;
  0;
  0;
]

(I'm not super well versed in lldb scripting -- but hopefully this is enough information to get the idea)

vadimcn commented 2 weeks ago

From what I've been able to tell there's no support for the equivalent of 'break on exception' in CodeLLDB.

It should be right there: image

Make sure you've set "sourceLanguages": ["cpp"] in your launch config. Or, rather, that you haven't reset it somewhere, because cpp is the default.