Age | Commit message (Collapse) | Author | Files | Lines |
|
Fixes some casing mistakes I added in #161581
|
|
This patch adds a load core time, right now we don't have much insight
into the performance of load core, especially for large coredumps. To
start collecting information on this I've added some minor
instrumentation code to measure the two call sites of `LoadCore`.
I've also added a test to validate the new metric is output in
statistics dump
|
|
The build script copies lldb-defines.h into the staging area but it gets
overwritten by version-header-fix.py. This flow assumes that the
lldb-defines.h from the source was writable originally (thus the copy
maintains that permission). This is problematic for systems that
integrate LLVM source as read only.
This change skips the initial copy of lldb-defines.h, which prevents
lldb build failures when the source is not writable.
|
|
[lldb] Track CFA pointer metadata in StackID
In this commit:
9c8e71644227 [lldb] Make StackID call Fix{Code,Data} pointers (#152796)
We made StackID keep track of the CFA without any pointer metadata in
it. This is necessary when comparing two StackIDs to determine which one
is "younger".
However, the CFA inside StackIDs is also used in other contexts through
the method StackID::GetCallFrameAddress. One notable case is
DWARFExpression: the computation of `DW_OP_call_frame_address` is done
using StackID. This feeds into many other places, e.g. expression
evaluation may require the address of a variable that is computed from
the CFA; to access the variable without faulting, we may need to
preserve the pointer metadata. As such, StackID must be able to provide
both versions of the CFA.
In the spirit of allowing consumers of pointers to decide what to do
with pointer metadata, this patch changes StackID to store both versions
of the cfa pointer. Two getter methods are provided, and all call sites
except DWARFExpression preserve their existing behavior (stripped
pointer). Other alternatives were considered:
* Just store the raw pointer. This would require changing the
comparisong operator `<` to also receive a Process, as the comparison
requires stripped pointers. It wasn't clear if all call-sites had a
non-null process, whereas we know we have a process when creating a
StackID.
* Store a weak pointer to the process inside the class, and then strip
metadata as needed. This would require a `weak_ptr::lock` in many
operations of LLDB, and it felt wasteful. It also prevents stripping
of the pointer if the process has gone away.
This patch also changes RegisterContextUnwind::ReadFrameAddress, which
is the method computing the CFA fed into StackID, to also preserve the
signature pointers.
|
|
This is something the StopInfo class manages, so it should be allowed to
compute this rather than having SBThread do so. This code just moves the
computation to methods in StopInfo. It is mostly NFC. The one change
that I actually had to adjust the tests for was a couple of tests that
were asking for the UnixSignal stop info data by asking for the data at
index 1. GetStopInfoDataCount returns 1 and we don't do 1 based indexing
so the test code was clearly wrong. But I don't think it makes sense to
perpetuate handing out the value regardless of what index you pass us.
|
|
The LLVM Style Guide says the following about error and warning messages
[1]:
> [T]o match error message styles commonly produced by other tools,
> start the first sentence with a lowercase letter, and finish the last
> sentence without a period, if it would end in one otherwise.
I often provide this feedback during code review, but we still have a
bunch of places where we have inconsistent error message, which bothers
me as a user. This PR identifies a handful of those places and updates
the messages to be consistent.
[1] https://llvm.org/docs/CodingStandards.html#error-and-warning-messages
|
|
This patch changes the way frames created from scripted affordances like
Scripted Threads are displayed. Currently, they're marked artificial
which is used usually for compiler generated frames.
This patch changes that behaviour by introducing a new synthetic
StackFrame kind and moves 'artificial' to be a distinct StackFrame
attribut.
On top of making these frames less confusing, this allows us to know
when a frame was created from a scripted affordance.
rdar://155949703
Signed-off-by: Med Ismail Bennani <ismail@bennani.ma>
|
|
This adjusts the ProtocolServer command to default to create a new
connection listening on `localhost:0` and adds a new `ServerMetadata`
details to `~/.lldb/mcp/lldb-<pid>.json` to record information about the
current MCP server.
This can be consumed by the lldb-mcp binary to establish a connection
from an LLM client.
---------
Co-authored-by: Jonas Devlieghere <jonas@devlieghere.com>
|
|
When you are trying for instance to set a breakpoint on a function by
name, but the SBFunction or SBSymbol are returning demangled names with
argument lists, that match can be tedious to do. Internally, the base
name of a symbol is something we handle all the time, so it's reasonable
that there should be a way to get that info from the API as well.
rdar://159318791
|
|
Re-land the symbol table feature in lldb-dap after it was
[reverted](https://github.com/llvm/llvm-project/commit/2b8e80694263fb404d1d0b816f33df731e617625)
because of a crash in the `aarch64` tests, which was caused by
dereferencing `SBSymbol::GetName` which might return `nullptr` for an
invalid symbol.
This patch reapplies the original commits and adds the missing null
check.
Also adding `skipIfWindows` for the module symbols tests, since LLDB
doesn't recognize the symbols from a.out there.
|
|
#140626 (#153836)"
This reverts commit 8b64cd8be29da9ea74db5a1a21f7cd6e75f9e9d8.
This breaks lldb-aarch64-* bots causing a crash in lldb-dap while
running test TestDAP_moduleSymbols.py
https://lab.llvm.org/buildbot/#/builders/59/builds/22959
https://lab.llvm.org/buildbot/#/builders/141/builds/10975
|
|
This patch adds setters to the SBStruturedData class to be able to
initialize said object from the client side directly.
Signed-off-by: Med Ismail Bennani <ismail@bennani.ma>
|
|
- VS Code extension:
- Add module symbol table viewer using
[Tabulator](https://tabulator.info/) for sorting and formatting rows.
- Add context menu action to the modules tree.
- lldb-dap
- Add `DAPGetModuleSymbolsRequest` to get symbols from a module.
Fixes #140626
[Screencast From 2025-08-15
19-12-33.webm](https://github.com/user-attachments/assets/75e2f229-ac82-487c-812e-3ea33a575b70)
|
|
added getName method in SBModule.h and .cpp in order to get the name of
the module from m_object_name.
---------
Co-authored-by: Bar Soloveychik <barsolo@fb.com>
|
|
The PR https://github.com/llvm/llvm-project/pull/152020 got merged by
accident. This commit addresses some follow up review comments.
|
|
Prior to this patch, SBFrame/SBThread methods exhibit racy behavior if
called while the process is running, because they do not lock the
`Process::RetRunLock` mutex. If they did, they would fail, correctly
identifying that the process is not running.
Some methods _attempt_ to protect against this with the pattern:
```
ExecutionContext exe_ctx(m_opaque_sp.get(), lock); // this is a different lock
Process *process = exe_ctx.GetProcessPtr();
if (process) {
Process::StopLocker stop_locker;
if (stop_locker.TryLock(&process->GetRunLock()))
.... do work ...
```
However, this is also racy: the constructor of `ExecutionContext` will
access the frame list, which is something that can only be done once the
process is stopped.
With this patch:
1. The constructor of `ExecutionContext` now expects a `ProcessRunLock`
as an argument. It attempts to lock the run lock, and only fills in
information about frames and threads if the lock can be acquired.
Callers of the constructor are expected to check the lock.
2. All uses of ExecutionContext are adjusted to conform to the above.
3. The SBThread.cpp-defined helper function ResumeNewPlan now expects a
locked ProcessRunLock as _proof_ that the execution is stopped. It will
unlock the mutex prior to resuming the process.
This commit exposes many opportunities for early-returns, but these
would increase the diff of this patch and distract from the important
changes, so we opt not to do it here.
|
|
Resolves #141955
- Adds data to breakpoints `Source` object, in order for assembly
breakpoints, which rely on a temporary `sourceReference` value, to be
able to resolve in future sessions like normal path+line breakpoints
- Adds optional `instructions_offset` parameter to `BreakpointResolver`
|
|
This *private* method is only defined as a member class of SBThread so
that it may be declared a `friend` of SBError and access a private
constructor of SBError that takes a `Status`, which is an `lldb_private`
object. In other words, the method does not use anything about the class
is belongs to, so it has no business being a member method.
A subsequent patch will need to add a new argument to this class, a
`Process::StopLocker`, which is also another `lldb_private` object. This
is another strong suggestion that this method does not belong on the
header file of a public API, even if at the private visibility level. We
can't forward declare nested types like `Process:StopLocker`, so this
problem is not easily solvable.
This commit moves the method out of the header and into an anon
namespace of the cpp file. It is also made to return a Status instead,
and the private `SBError` constructor is accessed by the SBThread
methods that call it.
|
|
The LLDB standalone build using Xcode currently fails due to the headers
being attached to multiple targets, but none of these targets depending
on each other. This commit resolves this by creating those dependencies.
|
|
This is a continuation of 68fd102, which did the same thing but only for
StopInfo. Using make_shared is both safer and more efficient:
- With make_shared, the object and the control block are allocated
together, which is more efficient.
- With make_shared, the enable_shared_from_this base class is properly
linked to the control block before the constructor finishes, so
shared_from_this() will be safe to use (though still not recommended
during construction).
|
|
This commit fixes the target that stages headers in the build dir's
include directory so that the headers are staged correctly so that the
target for liblldbrpc-headers can depend on it properly
|
|
When gathering the headers to fix up and place in LLDB.framework, we
were previously globbing the header files from a location in the build
directory. This commit changes this to glob from the source directory
instead, as we were globbing from the build directory without ensuring
that the necessary files were actually in that location before globbing.
|
|
### Context
Over a year ago, I landed support for 64b Memory ranges in Minidump
(#95312). In this patch we added the Memory64 list stream, which is
effectively a Linked List on disk. The layout is a sixteen byte header
and then however many Memory descriptors.
### The Bug
This is a classic off-by one error, where I added 8 bytes instead of 16
for the header. This caused the first region to start 8 bytes before the
correct RVA, thus shifting all memory reads by 8 bytes. We are correctly
writing all the regions to disk correctly, with no physical corruption
but the RVA is defined wrong, meaning we were incorrectly reading memory

### Why wasn't this caught?
One problem we've had is forcing Minidump to actually use the 64b mode,
it would be a massive waste of resources to have a test that actually
wrote >4.2gb of IO to validate the 64b regions, and so almost all
validation has been manual. As a weakness of manual testing, this issue
is psuedo non-deterministic, as what regions end up in 64b or 32b is
handled greedily and iterated in the order it's laid out in
/proc/pid/maps. We often validated 64b was written correctly by
hexdumping the Minidump itself, which was not corrupted (other than the
BaseRVA)

### Why is this showing up now?
During internal usage, we had a bug report that the Minidump wasn't
displaying values. I was unable to repro the issue, but during my
investigation I saw the variables were in the 64b regions which resulted
in me identifying the bug.
### How do we prevent future regressions?
To prevent regressions, and honestly to save my sanity for figuring out
where 8 bytes magically came from, I've added a new API to
SBSaveCoreOptions.
```SBSaveCoreOptions::GetMemoryRegionsToSave()```
The ability to get the memory regions that we intend to include in the Coredump. I added this so we can compare what we intended to include versus what was actually included. Traditionally we've always had issues comparing regions because Minidump includes `/proc/pid/maps` and it can be difficult to know what memoryregion read failure was a genuine error or just a page that wasn't meant to be included.
We are also leveraging this API to choose the memory regions to be generated, as well as for testing what regions should be bytewise 1:1.
After much debate with @clayborg, I've moved all non-stack memory to the Memory64 List. This list doesn't incur us any meaningful overhead and Greg originally suggested doing this in the original 64b PR. This also means we're exercising the 64b path every single time we save a Minidump, preventing regressions on this feature from slipping through testing in the future.
Snippet produced by [minidump.py](https://github.com/clayborg/scripts)
```
MINIDUMP_MEMORY_LIST:
NumberOfMemoryRanges = 0x00000002
MemoryRanges[0] = [0x00007f61085ff9f0 - 0x00007f6108601000) @ 0x0003f655
MemoryRanges[1] = [0x00007ffe47e50910 - 0x00007ffe47e52000) @ 0x00040c65
MINIDUMP_MEMORY64_LIST:
NumberOfMemoryRanges = 0x000000000000002e
BaseRva = 0x0000000000042669
MemoryRanges[0] = [0x00005584162d8000 - 0x00005584162d9000)
MemoryRanges[1] = [0x00005584162d9000 - 0x00005584162db000)
MemoryRanges[2] = [0x00005584162db000 - 0x00005584162dd000)
MemoryRanges[3] = [0x00005584162dd000 - 0x00005584162ff000)
MemoryRanges[4] = [0x00007f6100000000 - 0x00007f6100021000)
MemoryRanges[5] = [0x00007f6108800000 - 0x00007f6108828000)
MemoryRanges[6] = [0x00007f6108828000 - 0x00007f610899d000)
MemoryRanges[7] = [0x00007f610899d000 - 0x00007f61089f9000)
MemoryRanges[8] = [0x00007f61089f9000 - 0x00007f6108a08000)
MemoryRanges[9] = [0x00007f6108bf5000 - 0x00007f6108bf7000)
```
### Misc
As a part of this fix I had to look at LLDB logs a lot, you'll notice I added `0x` to many of the PRIx64 `LLDB_LOGF`. This is so the user (or I) can directly copy paste the address in the logs instead of adding the hex prefix themselves.
Added some SBSaveCore tests for the new GetMemoryAPI, and Docstrings.
CC: @DavidSpickett, @da-viper @labath because we've been working together on save-core plugins, review it optional and I didn't tag you but figured you'd want to know
|
|
LLDB breakpoint conditions take an expression that's evaluated using the
language of the code where the breakpoint is located. Users have asked
to have an option to tell it to evaluate the expression in a specific
language.
This is feature is especially helpful for Swift, for example for a
condition based on the value in memory at an offset from a register.
Such a condition is pretty difficult to write in Swift, but easy in C.
This PR adds a new argument (-Y) to specify the language of the
condition expression. We can't reuse the current -L option, since you
might want to break on only Swift symbols, but run a C expression there
as per the example above.
rdar://146119507
|
|
This RP changes some Breakpoint-related interfaces to return errors. On
its own these improvements are small, but they encourage better error
handling going forward. There are a bunch of other candidates, but these
were the functions that I touched while working on #146602.
|
|
This adds SBBreakpoint::SetIsHardware, allowing clients to mark an
existing breakpoint as a hardware breakpoint purely through the API.
This is safe to do after creation, as the hardware/software distinction
doesn't affect how breakpoint locations are selected.
In some cases (e.g. when writing a trap instruction would alter program
behavior), it's important to use hardware breakpoints. Ideally, we’d
extend the various `Create` methods to support this, but given their
number, this patch limits the scope to the post-creation API. As a
workaround, users can also rely on target.require-hardware-breakpoint or
use the `breakpoint set` command.
rdar://153528045
|
|
Using named args means that you don't need to keep track of 5 positional
args.
|
|
On Windows, the post build command would open the script in the default
editor, since it doesn't know about shebangs. This effectively adds
`python3` in front of the command.
Amends https://github.com/llvm/llvm-project/pull/142871 /
https://github.com/llvm/llvm-project/pull/141116
|
|
This relands the original commit for the versioning script in LLDB. This
commit uses '>' for output from `unifdef` for platforms that have that
executable but do not have the `-o` option. It also fixes the Xcode
build by adding a dependency between the liblldb-header-staging target
in the source/API/CMakeLists.txt the `liblldb-resource-headers` target
in LLDBFramework.cmake.
Original patch: https://github.com/llvm/llvm-project/pull/141116
|
|
Replace (questionable) header globs with an explicit argument supported
by llvm_add_library.
|
|
Reverts llvm/llvm-project#141116. It's breaking the Xcode build as well
as the build on AIX.
|
|
If we're not touching them, we don't need to do anything special to pass
them along -- with one important caveat: due to how cmake arguments
work, the implicitly passed arguments need to be specified before
arguments that we handle.
This isn't particularly nice, but the alternative is enumerating all
arguments that can be used by llvm_add_library and the macros it calls
(it also relies on implicit passing of some arguments to
llvm_process_sources).
|
|
This commit creates a Python script that fixes up the versioning
information in lldb-defines.h. It also moves the build logic for fixing
up the lldb headers from being in the framework only to being in the
same location that we create the liblldb target.
|
|
The problem was in calling GetLoadAddress on a value in the error state,
where `ValueObject::GetLoadAddress` could end up accessing the
uninitialized "address type" by-ref return value from `GetAddressOf`.
This probably happened because each function expected the other to
initialize it.
We can guarantee initialization by turning this into a proper return
value.
I've added a test, but it only (reliably) crashes if lldb is built with
ubsan.
|
|
Refactor code revolving source objects such that most logics will be
reused.
The main change is to expose a single `CreateSource(addr, target)` that
can return either a normal or an assembly source object, and call
`ShouldDisplayAssemblySource()` only from this function instead of
multiple places across the code.
Other functions can use `source.IsAssemblySource()` in order to check
which type the source is.
|
|
(#141424)
This is the currently the default for
`SBTarget::ReadInstructions(SBAddress, uint32_t)`. But not for others,
to make it consistent used the user assigned instruction flavour.
|
|
& few other files (#141478)
A few files of lldb dir & few other files had duplicate headers
included. This patch removes those redundancies.
---------
Co-authored-by: Akash Agrawal <akashag@qti.qualcomm.com>
|
|
Use the if statement with an initializer pattern that's very common in
LLVM in SBTarget. Every time someone adds a new method to SBTarget, I
want to encourage using this pattern, but I don't because it would be
inconsistent with the rest of the file. This solves that problem by
switching over the whole file.
|
|
command. (#138209)
Currently, the type `T`'s summary formatter will be matched for `T`,
`T*`, `T**` and so on. This is unexpected in many data formatters. Such
unhandled cases could cause the data formatter to crash. An example
would be the lldb's built-in data formatter for `std::optional`:
```
$ cat main.cpp
#include <optional>
int main() {
std::optional<int> o_null;
auto po_null = &o_null;
auto ppo_null = &po_null;
auto pppo_null = &ppo_null;
return 0;
}
$ clang++ -g main.cpp && lldb -o "b 8" -o "r" -o "v pppo_null"
[lldb crash]
```
This change adds an options `--pointer-match-depth` to `type summary
add` command to allow users to specify how many layer of pointers can be
dereferenced at most when matching a summary formatter of type `T`, as
Jim suggested
[here](https://github.com/llvm/llvm-project/pull/124048/#issuecomment-2611164133).
By default, this option has value 1 which means summary formatter for
`T` could also be used for `T*` but not `T**` nor beyond. This option is
no-op when `--skip-pointers` is set as well.
I didn't add such option for `type synthetic add`, `type format add`,
`type filter add`, because it useful for those command. Instead, they
all have the pointer match depth of 1. When printing a type `T*`, lldb
never print the children of `T` even if there is a synthetic formatter
registered for `T`.
|
|
Fix the handling of the `instructionOffset` parameter, which resulted in
always returning the wrong disassembly because VSCode always uses
`instructionOffset = -50` and expects 50 instructions before the given
address, instead of 50 bytes before
|
|
The motivation here is being (un)able to treat pointer values as an
array consistently. This works for pointers to simple/scalar values, but
for aggregates, we get a very surprising result:
- GetChildAtIndex(x, ??, true) returns the `x` child of the zeroth array
member (the one you get by dereferencing the pointer/array) for all `x`
which are smaller than the number of children of that value.
- for other values of `x`, we get `v[x]`, where `v` is treated like a
(C) pointer
This patch reimagines this interface so that the value of `true` always
treats (pointer and array) values as pointers. For `false`, we always
dereference pointers, while in the case of arrays, we only return the
values as far as the array bounds will allow.
This has the potential to break existing code, but I have a suspicion
that code was already broken to begin with, which is why I think this
would be better than introducing a new API and keeping the old (and
surprising) behavior. If our own test coverage is any indication,
breakage should be minimal.
|
|
|
|
bytes (#138169)
My current internal work requires some sensitivity to IO usage. I had a
work around to calculate the expected size of a Minidump, but I've added
this PR so an automated system could look at the expected size of an
LLDB generated Minidump and then choose if it has the space or wants to
generate it.
There are some prerequisites to calculating the correct size, so I have
the API take a reference for an SBError, I originally tried to return an
SBError and instead take a uint64_t reference, but this made the API
very difficult to use in python.
Added a test case as well.
|
|
SBThreadPlan (#137904)
Expose `QueueThreadPlanForStepSingleInstruction` function to
SBThreadPlan
|
|
This patch replaces the use of `UINT32_MAX` as the error return value of
`GetIndexOfChildWithName` with `llvm::Expected`.
# Tasks to do in another PR
1. Replace `CalculateNumChildrenIgnoringErrors` with
`CalculateNumChildren`. See [this
comment](https://github.com/llvm/llvm-project/pull/136693#discussion_r2056319358).
2. Update `lldb_private::formatters::ExtractIndexFromString` to use
`llvm::Expected`. See [this
comment](https://github.com/llvm/llvm-project/pull/136693#discussion_r2054217536).
3. Create a new class which carries both user and internal errors. See
[this
comment](https://github.com/llvm/llvm-project/pull/136693#discussion_r2056439608).
|
|
Errors will get "error:" prefixes automatically so this is not needed.
|
|
This patch is slightly different from other impl in that we dispatch
client-telemetry via a different helper method. This is to make it
easier for vendor to opt-out (simply by overriding the method to do
nothing). There is also a configuration option to disallow collecting
client telemetry.
---------
Co-authored-by: Pavel Labath <pavel@labath.sk>
|
|
- Adding `max_return_elements` field to `CompletionRequest`.
- adding maximum checks to `SymbolCompleter` and `SourceFileCompleter`.
Fixes #135553
|
|
Improves the lldb-dap disassembly by showing load addresses in
disassembly, same as in a regular LLDB `disassemble` command by default.
Before:

After:

|
|
# Summary
This PR updates `SBProcess::GetNumThreads()` and
`SBProcess::GetThreadAtIndex()` to listen to the stop locker.
`SBProcess::GetNumThreads()` will return 0 if the process is running.
## Problem Description
Recently upon debugging a program with thousands of threads in VS Code,
lldb-dap would hang at a `threads` request sent right after receiving
the `configurationDone` response. Soon after it will end the debug
session with the following error
```
Process <pid> exited with status = -1 (0xffffffff) lost connection
```
This is because LLDB is still in the middle of resuming all the threads.
And requesting threads will end up interrupt the process on Linux. From
the gdb-remote log it ended up getting `lldb::StateType::eStateInvalid`
and just exit with status -1.
I don't think it's reasonable to allow getting threads from a running
process. There are a few approaches to fix this:
1) Send the stopped event to IDE after `configurationDone`. This aligns
with the CLI behavior.
2) However, the above approach will break the existing user facing
behavior. The alternative will be reject the `threads` request if the
process is not stopped.
3) Improve the run lock. This is a synchronize issue where process was
in the middle of resuming while lldb-dap attempts to interrupt it.
**This PR implements the option 3**
## HOWEVER
This fixed the "lost connection" issue below but new issue has surfaced.
From testing, and also from checking the [VSCode source
code](https://github.com/microsoft/vscode/blob/174af221c9ea2ccdb64abe4aab8e1a805e77beae/src/vs/workbench/contrib/debug/browser/debugSession.ts#L791),
it expects having threadID to perform `pause`. So after attaching,
without any threads reported to the client, the user will not be able to
pause the attached process. `setBreakpoint` will still work and once we
make a stop at the bp (or any stop that will report threads, client can
perform pause again.
## NEXT
1) Made an attempt to return initial thread list so that VSCode can
pause (second commit in the PR)
2) Investigate why threads will trigger unwinding the second frame of a
thread, which leads to sending the interrupt
3) Decided if we want to support `stopOnEntry` for attaching, given
i. This is not an official specification
ii. If enable stopOnEntry, we need to fix attaching on Linux, to send
only one stopped event. Currently, all threads upon attaching will have
stop reason `SIGSTOP` and lldb-dap will send `stopped` event for each
one of them. Every `stopped` will trigger the client request for
threads.
iii. Alternatively, we can support auto continue correspond to `(lldb)
process attach --continue`. This require the ii above.
### Additionally
lldb-dap will not send a `continued` event after `configurationDone`
because it checks `dap.focus_tid == LLDB_INVALID_THREAD_ID` (so that we
don't send it for `launch` request). Notice `dap.focus_tid` will only
get assigned when handling stop or stepping.
According to DAP
> Please note: a debug adapter is not expected to send this event in
response to a request that implies that execution continues, e.g. launch
or continue.
It is only necessary to send a continued event if there was no previous
request that implied this.
So I guess we are not violating DAP if we don't send `continued` event.
But I'd like to get some sense about this.
## Test Plan
Used following program for testing:
https://gist.github.com/kusmour/1729d2e07b7b1063897db77de194e47d
**NOTE: Utilize stdin to get pid and attach AFTER hitting enter. Attach
should happen when all the threads start running.**
DAP messages before the change
<img width="1165" alt="image"
src="https://github.com/user-attachments/assets/a9ad85fb-81ce-419c-95e5-612639905c66"
/>
DAP message after the change - report zero threads after attaching
<img width="1165" alt="image"
src="https://github.com/user-attachments/assets/a1179e18-6844-437a-938c-0383702294cd"
/>
---------
Co-authored-by: Jonas Devlieghere <jonas@devlieghere.com>
|