Skip to content

Commit 7c7ee1a

Browse files
authored
Merge pull request #9436 from swiftlang/jdevlieghere/lldb-dap-cherrypicks
🍒 `lldb-dap` cherrypicks
2 parents eef0ef2 + 291a962 commit 7c7ee1a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+2729
-433
lines changed

lldb/docs/resources/lldbdap.md

Lines changed: 89 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,95 @@
1-
# LLDB-DAP
2-
3-
The `lldb-dap` tool (formerly `lldb-vscode`) is a command line tool that
4-
implements the [Debug Adapter
5-
Protocol](https://microsoft.github.io/debug-adapter-protocol/). It can be
6-
installed as an extension for Visual Studio Code and other IDEs supporting DAP.
7-
The protocol is easy to run remotely and also can allow other tools and IDEs to
8-
get a full featured debugger with a well defined protocol.
9-
10-
## Local Installation for Visual Studio Code
1+
# Contributing to LLDB-DAP
2+
3+
This guide describes how to extend and contribute to lldb-dap.
4+
For documentation on how to use lldb-dap, see [lldb-dap's README](https://github.com/llvm/llvm-project/blob/main/lldb/tools/lldb-dap/README.md).
5+
6+
lldb-dap and LLDB are developed under the umbrella of the
7+
[LLVM project](https://llvm.org/). As such, the
8+
"[Getting Started with the LLVM System](https://llvm.org/docs/GettingStarted.html)",
9+
"[Contributing to LLVM](https://llvm.org/docs/Contributing.html)" and
10+
"[LLVM coding standard](https://llvm.org/docs/CodingStandards.html)"
11+
guides might also be relevant, if you are a first-time contributor to the LLVM
12+
project.
13+
14+
lldb-dap's source code is [part of the LLVM
15+
repository](https://github.com/llvm/llvm-project/tree/main/lldb/tools/lldb-dap)
16+
on Github. We use Github's [issue
17+
tracker](https://github.com/llvm/llvm-project/tree/main/lldb/tools/lldb-dap)
18+
and patches can be submitted via [pull
19+
requests](https://github.com/llvm/llvm-project/pulls).
20+
21+
## Building `lldb-dap` from soruce
22+
23+
To build lldb-dap from source, first need to [setup a LLDB build](https://lldb.llvm.org/resources/build.html).
24+
After doing so, run `ninja lldb-dap`. To use your freshly built `lldb-dap`
25+
binary, install the VS Code extension and point it to lldb-dap by setting the
26+
`lldb-dap.executable-path` setting.
27+
28+
## Responsibilities of LLDB, lldb-dap and the Visual Studio Code Extension
29+
30+
Under the hood, the UI-based debugging experience is fueled by three separate
31+
components:
32+
33+
* LLDB provides general, IDE-indepedent debugging features, such as:
34+
loading binaries / core dumps, interpreting debug info, setting breakpoints,
35+
pretty-printing variables, etc. The `lldb` binary exposes this functionality
36+
via a command line interface.
37+
* `lldb-dap` exposes LLDB's functionality via the
38+
"[Debug Adapter Protocol](https://microsoft.github.io/debug-adapter-protocol/)",
39+
i.e. a protocol through which various IDEs (VS Code, Emacs, vim, neovim, ...)
40+
can interact with a wide range of debuggers (`lldb-dap` and many others).
41+
* The VS Code extension exposes the lldb-dap binary within VS Code. It acts
42+
as a thin wrapper around the lldb-dap binary, and adds VS-Code-specific UI
43+
integration on top of lldb-dap, such as autocompletion for `launch.json`
44+
configuration files.
45+
46+
Since lldb-dap builds on top of LLDB, all of LLDB's extensibility mechanisms
47+
such as [Variable Pretty-Printing](https://lldb.llvm.org/use/variable.html),
48+
[Frame recognizers](https://lldb.llvm.org/use/python-reference.html#writing-lldb-frame-recognizers-in-python)
49+
and [Python Scripting](https://lldb.llvm.org/use/python.html) are available
50+
also in lldb-dap.
51+
52+
When adding new functionality, you generally want to add it on the lowest
53+
applicable level. I.e., quite frequently you actually want to add functionality
54+
to LLDB's core in order to improve your debugging experience in VS Code.
55+
56+
### The Debug Adapter Protocol
57+
58+
The most relevant resources for the Debug Adapter Protocol are:
59+
* [The overview](https://microsoft.github.io/debug-adapter-protocol/overview)
60+
which provides a high-level introduction,
61+
* the [human-readable specification](https://microsoft.github.io/debug-adapter-protocol/specification), and
62+
* the [JSON-schema specification](https://github.com/microsoft/debug-adapter-protocol/blob/main/debugAdapterProtocol.json).
63+
64+
lldb-dap adds some additional non-standard extensions to the protocol. To take
65+
advantage of those extensions, IDE-specific support code is needed, usually
66+
inside the VS Code extension. When adding a new extension, please first look
67+
through the [issue tracker of the Debug Adapter
68+
Protocol](https://github.com/microsoft/debug-adapter-protocol/issues) to check
69+
if there already is a proposal serving your use case. If so, try to take
70+
inspiration from it. If not, consider opening an upstream issue.
71+
72+
To avoid naming collisions with potential future extensions of the Debug
73+
Adapter protocol, all non-standard extensions should use the prefix
74+
`$__lldb_extension` in their JSON property names.
75+
76+
### Debugging the Debug Adapter Protocol
77+
78+
To debug the Debug Adapter Protocol, point the `LLDBDAP_LOG` environment
79+
variable to a file on your disk. lldb-dap will log all communication received
80+
from / sent to the IDE to the provided path. In the VS Code extension, you
81+
can also set the log path through the `lldb-dap.log-path` VS Code setting.
82+
83+
## Building the VS Code extension from source
1184

1285
Installing the plug-in is very straightforward and involves just a few steps.
1386

87+
In most cases, installing the VS Code extension from the [VS Code
88+
Marketplace](https://marketplace.visualstudio.com/items?itemName=llvm-vs-code-extensions.lldb-dap)
89+
and pointing it to a locally built lldb-dap binary is sufficient. Building
90+
the VS Code extension from source is only necessary if the TypeScript code is
91+
changed.
92+
1493
### Pre-requisites
1594

1695
- Install a modern version of node (e.g. `v20.0.0`).
@@ -33,10 +112,6 @@ And then you are ready!
33112

34113
### Updating the extension
35114

36-
*Note: It's not necessary to update the extension if there has been changes
37-
to `lldb-dap`. The extension needs to be updated only if the TypesScript code
38-
has changed.*
39-
40115
Updating the extension is pretty much the same process as installing it from
41116
scratch. However, VS Code expects the version number of the upgraded extension
42117
to be greater than the previous one, otherwise the installation step might have

lldb/packages/Python/lldbsuite/test/lldbplatformutil.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,22 @@ def findMainThreadCheckerDylib():
180180
return ""
181181

182182

183+
def findBacktraceRecordingDylib():
184+
if not platformIsDarwin():
185+
return ""
186+
187+
if getPlatform() in lldbplatform.translate(lldbplatform.darwin_embedded):
188+
return "/Developer/usr/lib/libBacktraceRecording.dylib"
189+
190+
with os.popen("xcode-select -p") as output:
191+
xcode_developer_path = output.read().strip()
192+
mtc_dylib_path = "%s/usr/lib/libBacktraceRecording.dylib" % xcode_developer_path
193+
if os.path.isfile(mtc_dylib_path):
194+
return mtc_dylib_path
195+
196+
return ""
197+
198+
183199
class _PlatformContext(object):
184200
"""Value object class which contains platform-specific options."""
185201

lldb/packages/Python/lldbsuite/test/tools/lldb-dap/dap_server.py

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -691,6 +691,19 @@ def request_disassemble(
691691
for inst in instructions:
692692
self.disassembled_instructions[inst["address"]] = inst
693693

694+
def request_readMemory(self, memoryReference, offset, count):
695+
args_dict = {
696+
"memoryReference": memoryReference,
697+
"offset": offset,
698+
"count": count,
699+
}
700+
command_dict = {
701+
"command": "readMemory",
702+
"type": "request",
703+
"arguments": args_dict,
704+
}
705+
return self.send_recv(command_dict)
706+
694707
def request_evaluate(self, expression, frameIndex=0, threadId=None, context=None):
695708
stackFrame = self.get_stackFrame(frameIndex=frameIndex, threadId=threadId)
696709
if stackFrame is None:
@@ -707,6 +720,17 @@ def request_evaluate(self, expression, frameIndex=0, threadId=None, context=None
707720
}
708721
return self.send_recv(command_dict)
709722

723+
def request_exceptionInfo(self, threadId=None):
724+
if threadId is None:
725+
threadId = self.get_thread_id()
726+
args_dict = {"threadId": threadId}
727+
command_dict = {
728+
"command": "exceptionInfo",
729+
"type": "request",
730+
"arguments": args_dict,
731+
}
732+
return self.send_recv(command_dict)
733+
710734
def request_initialize(self, sourceInitFile):
711735
command_dict = {
712736
"command": "initialize",
@@ -754,6 +778,7 @@ def request_launch(
754778
runInTerminal=False,
755779
postRunCommands=None,
756780
enableAutoVariableSummaries=False,
781+
displayExtendedBacktrace=False,
757782
enableSyntheticChildDebugging=False,
758783
commandEscapePrefix=None,
759784
customFrameFormat=None,
@@ -806,6 +831,7 @@ def request_launch(
806831

807832
args_dict["enableAutoVariableSummaries"] = enableAutoVariableSummaries
808833
args_dict["enableSyntheticChildDebugging"] = enableSyntheticChildDebugging
834+
args_dict["displayExtendedBacktrace"] = displayExtendedBacktrace
809835
args_dict["commandEscapePrefix"] = commandEscapePrefix
810836
command_dict = {"command": "launch", "type": "request", "arguments": args_dict}
811837
response = self.send_recv(command_dict)
@@ -816,17 +842,21 @@ def request_launch(
816842
self.wait_for_event(filter=["process", "initialized"])
817843
return response
818844

819-
def request_next(self, threadId):
845+
def request_next(self, threadId, granularity="statement"):
820846
if self.exit_status is not None:
821847
raise ValueError("request_continue called after process exited")
822-
args_dict = {"threadId": threadId}
848+
args_dict = {"threadId": threadId, "granularity": granularity}
823849
command_dict = {"command": "next", "type": "request", "arguments": args_dict}
824850
return self.send_recv(command_dict)
825851

826-
def request_stepIn(self, threadId, targetId):
852+
def request_stepIn(self, threadId, targetId, granularity="statement"):
827853
if self.exit_status is not None:
828854
raise ValueError("request_stepIn called after process exited")
829-
args_dict = {"threadId": threadId, "targetId": targetId}
855+
args_dict = {
856+
"threadId": threadId,
857+
"targetId": targetId,
858+
"granularity": granularity,
859+
}
830860
command_dict = {"command": "stepIn", "type": "request", "arguments": args_dict}
831861
return self.send_recv(command_dict)
832862

@@ -976,7 +1006,7 @@ def request_compileUnits(self, moduleId):
9761006
return response
9771007

9781008
def request_completions(self, text, frameId=None):
979-
args_dict = {"text": text, "column": len(text)}
1009+
args_dict = {"text": text, "column": len(text) + 1}
9801010
if frameId:
9811011
args_dict["frameId"] = frameId
9821012
command_dict = {
@@ -1079,6 +1109,17 @@ def request_setVariable(self, containingVarRef, name, value, id=None):
10791109
}
10801110
return self.send_recv(command_dict)
10811111

1112+
def request_locations(self, locationReference):
1113+
args_dict = {
1114+
"locationReference": locationReference,
1115+
}
1116+
command_dict = {
1117+
"command": "locations",
1118+
"type": "request",
1119+
"arguments": args_dict,
1120+
}
1121+
return self.send_recv(command_dict)
1122+
10821123
def request_testGetTargetBreakpoints(self):
10831124
"""A request packet used in the LLDB test suite to get all currently
10841125
set breakpoint infos for all breakpoints currently set in the
@@ -1095,6 +1136,20 @@ def terminate(self):
10951136
self.send.close()
10961137
# self.recv.close()
10971138

1139+
def request_setInstructionBreakpoints(self, memory_reference=[]):
1140+
breakpoints = []
1141+
for i in memory_reference:
1142+
args_dict = {
1143+
"instructionReference": i,
1144+
}
1145+
breakpoints.append(args_dict)
1146+
args_dict = {"breakpoints": breakpoints}
1147+
command_dict = {
1148+
"command": "setInstructionBreakpoints",
1149+
"type": "request",
1150+
"arguments": args_dict,
1151+
}
1152+
return self.send_recv(command_dict)
10981153

10991154
class DebugAdaptorServer(DebugCommunication):
11001155
def __init__(

lldb/packages/Python/lldbsuite/test/tools/lldb-dap/lldbdap_testcase.py

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,10 @@ def verify_breakpoint_hit(self, breakpoint_ids):
8181
body = stopped_event["body"]
8282
if "reason" not in body:
8383
continue
84-
if body["reason"] != "breakpoint":
84+
if (
85+
body["reason"] != "breakpoint"
86+
and body["reason"] != "instruction breakpoint"
87+
):
8588
continue
8689
if "description" not in body:
8790
continue
@@ -100,13 +103,14 @@ def verify_breakpoint_hit(self, breakpoint_ids):
100103
return
101104
self.assertTrue(False, "breakpoint not hit")
102105

103-
def verify_stop_exception_info(self, expected_description):
106+
def verify_stop_exception_info(self, expected_description, timeout=timeoutval):
104107
"""Wait for the process we are debugging to stop, and verify the stop
105108
reason is 'exception' and that the description matches
106109
'expected_description'
107110
"""
108-
stopped_events = self.dap_server.wait_for_stopped()
111+
stopped_events = self.dap_server.wait_for_stopped(timeout=timeout)
109112
for stopped_event in stopped_events:
113+
print("stopped_event", stopped_event)
110114
if "body" in stopped_event:
111115
body = stopped_event["body"]
112116
if "reason" not in body:
@@ -177,6 +181,10 @@ def get_stackFrames(self, threadId=None, startFrame=None, levels=None, dump=Fals
177181
)
178182
return stackFrames
179183

184+
def get_exceptionInfo(self, threadId=None):
185+
response = self.dap_server.request_exceptionInfo(threadId=threadId)
186+
return self.get_dict_value(response, ["body"])
187+
180188
def get_source_and_line(self, threadId=None, frameIndex=0):
181189
stackFrames = self.get_stackFrames(
182190
threadId=threadId, startFrame=frameIndex, levels=1
@@ -202,6 +210,11 @@ def collect_console(self, timeout_secs, pattern=None):
202210
"console", timeout_secs=timeout_secs, pattern=pattern
203211
)
204212

213+
def collect_stdout(self, timeout_secs, pattern=None):
214+
return self.dap_server.collect_output(
215+
"stdout", timeout_secs=timeout_secs, pattern=pattern
216+
)
217+
205218
def get_local_as_int(self, name, threadId=None):
206219
value = self.dap_server.get_local_variable_value(name, threadId=threadId)
207220
# 'value' may have the variable value and summary.
@@ -222,14 +235,18 @@ def set_global(self, name, value, id=None):
222235
"""Set a top level global variable only."""
223236
return self.dap_server.request_setVariable(2, name, str(value), id=id)
224237

225-
def stepIn(self, threadId=None, targetId=None, waitForStop=True):
226-
self.dap_server.request_stepIn(threadId=threadId, targetId=targetId)
238+
def stepIn(
239+
self, threadId=None, targetId=None, waitForStop=True, granularity="statement"
240+
):
241+
self.dap_server.request_stepIn(
242+
threadId=threadId, targetId=targetId, granularity=granularity
243+
)
227244
if waitForStop:
228245
return self.dap_server.wait_for_stopped()
229246
return None
230247

231-
def stepOver(self, threadId=None, waitForStop=True):
232-
self.dap_server.request_next(threadId=threadId)
248+
def stepOver(self, threadId=None, waitForStop=True, granularity="statement"):
249+
self.dap_server.request_next(threadId=threadId, granularity=granularity)
233250
if waitForStop:
234251
return self.dap_server.wait_for_stopped()
235252
return None
@@ -369,6 +386,7 @@ def launch(
369386
expectFailure=False,
370387
postRunCommands=None,
371388
enableAutoVariableSummaries=False,
389+
displayExtendedBacktrace=False,
372390
enableSyntheticChildDebugging=False,
373391
commandEscapePrefix=None,
374392
customFrameFormat=None,
@@ -410,6 +428,7 @@ def cleanup():
410428
runInTerminal=runInTerminal,
411429
postRunCommands=postRunCommands,
412430
enableAutoVariableSummaries=enableAutoVariableSummaries,
431+
displayExtendedBacktrace=displayExtendedBacktrace,
413432
enableSyntheticChildDebugging=enableSyntheticChildDebugging,
414433
commandEscapePrefix=commandEscapePrefix,
415434
customFrameFormat=customFrameFormat,
@@ -449,6 +468,7 @@ def build_and_launch(
449468
postRunCommands=None,
450469
lldbDAPEnv=None,
451470
enableAutoVariableSummaries=False,
471+
displayExtendedBacktrace=False,
452472
enableSyntheticChildDebugging=False,
453473
commandEscapePrefix=None,
454474
customFrameFormat=None,
@@ -485,6 +505,7 @@ def build_and_launch(
485505
postRunCommands=postRunCommands,
486506
enableAutoVariableSummaries=enableAutoVariableSummaries,
487507
enableSyntheticChildDebugging=enableSyntheticChildDebugging,
508+
displayExtendedBacktrace=displayExtendedBacktrace,
488509
commandEscapePrefix=commandEscapePrefix,
489510
customFrameFormat=customFrameFormat,
490511
customThreadFormat=customThreadFormat,

0 commit comments

Comments
 (0)