Skip to content

Commit 0b8a656

Browse files
authored
[lldb-dap] Fix source references (#144364)
The [protocol](https://microsoft.github.io/debug-adapter-protocol//specification.html#Types_Source) expects that `sourceReference` be less than `(2^31)-1`, but we currently represent memory address as source reference, this can be truncated either when sending through json or by the client. Instead, generate new source references based on the memory address. Make the `ResolveSource` function return an optional source.
1 parent 5c310d1 commit 0b8a656

16 files changed

+201
-110
lines changed

lldb/test/API/tools/lldb-dap/breakpoint-assembly/TestDAP_breakpointAssembly.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -67,19 +67,19 @@ def test_break_on_invalid_source_reference(self):
6767
"Invalid sourceReference.",
6868
)
6969

70-
# Verify that setting a breakpoint on a source reference without a symbol also fails
70+
# Verify that setting a breakpoint on a source reference that is not created fails
7171
response = self.dap_server.request_setBreakpoints(
72-
Source(source_reference=0), [1]
72+
Source(source_reference=200), [1]
7373
)
7474
self.assertIsNotNone(response)
7575
breakpoints = response["body"]["breakpoints"]
7676
self.assertEqual(len(breakpoints), 1)
77-
breakpoint = breakpoints[0]
77+
break_point = breakpoints[0]
7878
self.assertFalse(
79-
breakpoint["verified"], "Expected breakpoint to not be verified"
79+
break_point["verified"], "Expected breakpoint to not be verified"
8080
)
81-
self.assertIn("message", breakpoint, "Expected message to be present")
81+
self.assertIn("message", break_point, "Expected message to be present")
8282
self.assertEqual(
83-
breakpoint["message"],
84-
"Breakpoints in assembly without a valid symbol are not supported yet.",
83+
break_point["message"],
84+
"Invalid sourceReference.",
8585
)

lldb/tools/lldb-dap/Breakpoint.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ protocol::Breakpoint Breakpoint::ToProtocolBreakpoint() {
6464
"0x" + llvm::utohexstr(bp_addr.GetLoadAddress(m_bp.GetTarget()));
6565
breakpoint.instructionReference = formatted_addr;
6666

67-
auto source = CreateSource(bp_addr, m_dap.target);
68-
if (!IsAssemblySource(source)) {
67+
std::optional<protocol::Source> source = m_dap.ResolveSource(bp_addr);
68+
if (source && !IsAssemblySource(*source)) {
6969
auto line_entry = bp_addr.GetLineEntry();
7070
const auto line = line_entry.GetLine();
7171
if (line != LLDB_INVALID_LINE_NUMBER)

lldb/tools/lldb-dap/DAP.cpp

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,27 @@ DAP::SendFormattedOutput(OutputType o, const char *format, ...) {
497497
o, llvm::StringRef(buffer, std::min<int>(actual_length, sizeof(buffer))));
498498
}
499499

500+
int32_t DAP::CreateSourceReference(lldb::addr_t address) {
501+
std::lock_guard<std::mutex> guard(m_source_references_mutex);
502+
auto iter = llvm::find(m_source_references, address);
503+
if (iter != m_source_references.end())
504+
return std::distance(m_source_references.begin(), iter) + 1;
505+
506+
m_source_references.emplace_back(address);
507+
return static_cast<int32_t>(m_source_references.size());
508+
}
509+
510+
std::optional<lldb::addr_t> DAP::GetSourceReferenceAddress(int32_t reference) {
511+
std::lock_guard<std::mutex> guard(m_source_references_mutex);
512+
if (reference <= LLDB_DAP_INVALID_SRC_REF)
513+
return std::nullopt;
514+
515+
if (static_cast<size_t>(reference) > m_source_references.size())
516+
return std::nullopt;
517+
518+
return m_source_references[reference - 1];
519+
}
520+
500521
ExceptionBreakpoint *DAP::GetExceptionBPFromStopReason(lldb::SBThread &thread) {
501522
const auto num = thread.GetStopReasonDataCount();
502523
// Check to see if have hit an exception breakpoint and change the
@@ -602,6 +623,55 @@ ReplMode DAP::DetectReplMode(lldb::SBFrame frame, std::string &expression,
602623
llvm_unreachable("enum cases exhausted.");
603624
}
604625

626+
std::optional<protocol::Source> DAP::ResolveSource(lldb::SBAddress address) {
627+
if (DisplayAssemblySource(debugger, address))
628+
return ResolveAssemblySource(address);
629+
630+
lldb::SBLineEntry line_entry = GetLineEntryForAddress(target, address);
631+
if (!line_entry.IsValid())
632+
return std::nullopt;
633+
634+
return CreateSource(line_entry.GetFileSpec());
635+
}
636+
637+
std::optional<protocol::Source>
638+
DAP::ResolveAssemblySource(lldb::SBAddress address) {
639+
lldb::SBSymbol symbol = address.GetSymbol();
640+
lldb::addr_t load_addr = LLDB_INVALID_ADDRESS;
641+
std::string name;
642+
if (symbol.IsValid()) {
643+
load_addr = symbol.GetStartAddress().GetLoadAddress(target);
644+
name = symbol.GetName();
645+
} else {
646+
load_addr = address.GetLoadAddress(target);
647+
name = GetLoadAddressString(load_addr);
648+
}
649+
650+
if (load_addr == LLDB_INVALID_ADDRESS)
651+
return std::nullopt;
652+
653+
protocol::Source source;
654+
source.sourceReference = CreateSourceReference(load_addr);
655+
lldb::SBModule module = address.GetModule();
656+
if (module.IsValid()) {
657+
lldb::SBFileSpec file_spec = module.GetFileSpec();
658+
if (file_spec.IsValid()) {
659+
std::string path = GetSBFileSpecPath(file_spec);
660+
if (!path.empty())
661+
source.path = path + '`' + name;
662+
}
663+
}
664+
665+
source.name = std::move(name);
666+
667+
// Mark the source as deemphasized since users will only be able to view
668+
// assembly for these frames.
669+
source.presentationHint =
670+
protocol::Source::eSourcePresentationHintDeemphasize;
671+
672+
return source;
673+
}
674+
605675
bool DAP::RunLLDBCommands(llvm::StringRef prefix,
606676
llvm::ArrayRef<std::string> commands) {
607677
bool required_command_failed = false;

lldb/tools/lldb-dap/DAP.h

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,9 @@ struct DAP {
219219
void __attribute__((format(printf, 3, 4)))
220220
SendFormattedOutput(OutputType o, const char *format, ...);
221221

222-
static int64_t GetNextSourceReference();
222+
int32_t CreateSourceReference(lldb::addr_t address);
223+
224+
std::optional<lldb::addr_t> GetSourceReferenceAddress(int32_t reference);
223225

224226
ExceptionBreakpoint *GetExceptionBPFromStopReason(lldb::SBThread &thread);
225227

@@ -252,6 +254,29 @@ struct DAP {
252254
ReplMode DetectReplMode(lldb::SBFrame frame, std::string &expression,
253255
bool partial_expression);
254256

257+
/// Create a "Source" JSON object as described in the debug adapter
258+
/// definition.
259+
///
260+
/// \param[in] address
261+
/// The address to use when populating out the "Source" object.
262+
///
263+
/// \return
264+
/// An optional "Source" JSON object that follows the formal JSON
265+
/// definition outlined by Microsoft.
266+
std::optional<protocol::Source> ResolveSource(lldb::SBAddress address);
267+
268+
/// Create a "Source" JSON object as described in the debug adapter
269+
/// definition.
270+
///
271+
/// \param[in] address
272+
/// The address to use when populating out the "Source" object.
273+
///
274+
/// \return
275+
/// An optional "Source" JSON object that follows the formal JSON
276+
/// definition outlined by Microsoft.
277+
std::optional<protocol::Source>
278+
ResolveAssemblySource(lldb::SBAddress address);
279+
255280
/// \return
256281
/// \b false if a fatal error was found while executing these commands,
257282
/// according to the rules of \a LLDBUtils::RunLLDBCommands.
@@ -406,6 +431,10 @@ struct DAP {
406431
std::thread progress_event_thread;
407432
/// @}
408433

434+
/// List of addresses mapped by sourceReference.
435+
std::vector<lldb::addr_t> m_source_references;
436+
std::mutex m_source_references_mutex;
437+
409438
/// Queue for all incoming messages.
410439
std::deque<protocol::Message> m_queue;
411440
std::mutex m_queue_mutex;

lldb/tools/lldb-dap/Handler/DisassembleRequestHandler.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,8 @@ static lldb::SBAddress GetDisassembleStartAddress(lldb::SBTarget target,
8585
}
8686

8787
static DisassembledInstruction ConvertSBInstructionToDisassembledInstruction(
88-
lldb::SBTarget &target, lldb::SBInstruction &inst, bool resolve_symbols) {
88+
DAP &dap, lldb::SBInstruction &inst, bool resolve_symbols) {
89+
lldb::SBTarget target = dap.target;
8990
if (!inst.IsValid())
9091
return GetInvalidInstruction();
9192

@@ -138,14 +139,14 @@ static DisassembledInstruction ConvertSBInstructionToDisassembledInstruction(
138139
si << " ; " << c;
139140
}
140141

141-
protocol::Source source = CreateSource(addr, target);
142+
std::optional<protocol::Source> source = dap.ResolveSource(addr);
142143
lldb::SBLineEntry line_entry = GetLineEntryForAddress(target, addr);
143144

144145
// If the line number is 0 then the entry represents a compiler generated
145146
// location.
146-
if (!IsAssemblySource(source) && line_entry.GetStartAddress() == addr &&
147-
line_entry.IsValid() && line_entry.GetFileSpec().IsValid() &&
148-
line_entry.GetLine() != 0) {
147+
if (source && !IsAssemblySource(*source) &&
148+
line_entry.GetStartAddress() == addr && line_entry.IsValid() &&
149+
line_entry.GetFileSpec().IsValid() && line_entry.GetLine() != 0) {
149150

150151
disassembled_inst.location = std::move(source);
151152
const auto line = line_entry.GetLine();
@@ -221,7 +222,7 @@ DisassembleRequestHandler::Run(const DisassembleArguments &args) const {
221222
original_address_index = i;
222223

223224
instructions.push_back(ConvertSBInstructionToDisassembledInstruction(
224-
dap.target, inst, resolve_symbols));
225+
dap, inst, resolve_symbols));
225226
}
226227

227228
// Check if we miss instructions at the beginning.

lldb/tools/lldb-dap/Handler/LocationsRequestHandler.cpp

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,16 @@ void LocationsRequestHandler::operator()(
137137
return;
138138
}
139139

140-
body.try_emplace("source", CreateSource(line_entry.GetFileSpec()));
140+
const std::optional<protocol::Source> source =
141+
CreateSource(line_entry.GetFileSpec());
142+
if (!source) {
143+
response["success"] = false;
144+
response["message"] = "Failed to resolve file path for location";
145+
dap.SendJSON(llvm::json::Value(std::move(response)));
146+
return;
147+
}
148+
149+
body.try_emplace("source", *source);
141150
if (int line = line_entry.GetLine())
142151
body.try_emplace("line", line);
143152
if (int column = line_entry.GetColumn())
@@ -152,7 +161,16 @@ void LocationsRequestHandler::operator()(
152161
return;
153162
}
154163

155-
body.try_emplace("source", CreateSource(decl.GetFileSpec()));
164+
const std::optional<protocol::Source> source =
165+
CreateSource(decl.GetFileSpec());
166+
if (!source) {
167+
response["success"] = false;
168+
response["message"] = "Failed to resolve file path for location";
169+
dap.SendJSON(llvm::json::Value(std::move(response)));
170+
return;
171+
}
172+
173+
body.try_emplace("source", *source);
156174
if (int line = decl.GetLine())
157175
body.try_emplace("line", line);
158176
if (int column = decl.GetColumn())

lldb/tools/lldb-dap/Handler/SourceRequestHandler.cpp

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,34 +29,42 @@ namespace lldb_dap {
2929
/// the source code for a given source reference.
3030
llvm::Expected<protocol::SourceResponseBody>
3131
SourceRequestHandler::Run(const protocol::SourceArguments &args) const {
32-
const auto source =
32+
33+
uint32_t source_ref =
3334
args.source->sourceReference.value_or(args.sourceReference);
35+
const std::optional<lldb::addr_t> source_addr_opt =
36+
dap.GetSourceReferenceAddress(source_ref);
3437

35-
if (!source)
38+
if (!source_addr_opt)
3639
return llvm::make_error<DAPError>(
37-
"invalid arguments, expected source.sourceReference to be set");
40+
llvm::formatv("Unknown source reference {}", source_ref));
3841

39-
lldb::SBAddress address(source, dap.target);
42+
lldb::SBAddress address(*source_addr_opt, dap.target);
4043
if (!address.IsValid())
4144
return llvm::make_error<DAPError>("source not found");
4245

4346
lldb::SBSymbol symbol = address.GetSymbol();
44-
45-
lldb::SBStream stream;
46-
lldb::SBExecutionContext exe_ctx(dap.target);
47+
lldb::SBInstructionList insts;
4748

4849
if (symbol.IsValid()) {
49-
lldb::SBInstructionList insts = symbol.GetInstructions(dap.target);
50-
insts.GetDescription(stream, exe_ctx);
50+
insts = symbol.GetInstructions(dap.target);
5151
} else {
5252
// No valid symbol, just return the disassembly.
53-
lldb::SBInstructionList insts = dap.target.ReadInstructions(
53+
insts = dap.target.ReadInstructions(
5454
address, dap.k_number_of_assembly_lines_for_nodebug);
55-
insts.GetDescription(stream, exe_ctx);
5655
}
5756

57+
if (!insts || insts.GetSize() == 0)
58+
return llvm::make_error<DAPError>(
59+
llvm::formatv("no instruction source for address {}",
60+
address.GetLoadAddress(dap.target)));
61+
62+
lldb::SBStream stream;
63+
lldb::SBExecutionContext exe_ctx(dap.target);
64+
insts.GetDescription(stream, exe_ctx);
5865
return protocol::SourceResponseBody{/*content=*/stream.GetData(),
59-
/*mimeType=*/"text/x-lldb.disassembly"};
66+
/*mimeType=*/
67+
"text/x-lldb.disassembly"};
6068
}
6169

6270
} // namespace lldb_dap

lldb/tools/lldb-dap/Handler/StackTraceRequestHandler.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ static bool FillStackFrames(DAP &dap, lldb::SBThread &thread,
6969
break;
7070
}
7171

72-
stack_frames.emplace_back(CreateStackFrame(frame, frame_format));
72+
stack_frames.emplace_back(CreateStackFrame(dap, frame, frame_format));
7373
}
7474

7575
if (include_all && reached_end_of_stack) {

lldb/tools/lldb-dap/JSONUtils.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -543,7 +543,7 @@ llvm::json::Object CreateEventObject(const llvm::StringRef event_name) {
543543
// },
544544
// "required": [ "id", "name", "line", "column" ]
545545
// }
546-
llvm::json::Value CreateStackFrame(lldb::SBFrame &frame,
546+
llvm::json::Value CreateStackFrame(DAP &dap, lldb::SBFrame &frame,
547547
lldb::SBFormat &format) {
548548
llvm::json::Object object;
549549
int64_t frame_id = MakeDAPFrameID(frame);
@@ -573,8 +573,10 @@ llvm::json::Value CreateStackFrame(lldb::SBFrame &frame,
573573
EmplaceSafeString(object, "name", frame_name);
574574

575575
auto target = frame.GetThread().GetProcess().GetTarget();
576-
auto source = CreateSource(frame.GetPCAddress(), target);
577-
if (!IsAssemblySource(source)) {
576+
std::optional<protocol::Source> source =
577+
dap.ResolveSource(frame.GetPCAddress());
578+
579+
if (source && !IsAssemblySource(*source)) {
578580
// This is a normal source with a valid line entry.
579581
auto line_entry = frame.GetLineEntry();
580582
object.try_emplace("line", line_entry.GetLine());
@@ -598,7 +600,8 @@ llvm::json::Value CreateStackFrame(lldb::SBFrame &frame,
598600
object.try_emplace("column", 1);
599601
}
600602

601-
object.try_emplace("source", std::move(source));
603+
if (source)
604+
object.try_emplace("source", std::move(source).value());
602605

603606
const auto pc = frame.GetPC();
604607
if (pc != LLDB_INVALID_ADDRESS) {

lldb/tools/lldb-dap/JSONUtils.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,9 @@ llvm::json::Object CreateEventObject(const llvm::StringRef event_name);
234234
/// "line" - the source file line number as an integer
235235
/// "column" - the source file column number as an integer
236236
///
237+
/// \param[in] dap
238+
/// The DAP session associated with the stopped thread.
239+
///
237240
/// \param[in] frame
238241
/// The LLDB stack frame to use when populating out the "StackFrame"
239242
/// object.
@@ -245,7 +248,7 @@ llvm::json::Object CreateEventObject(const llvm::StringRef event_name);
245248
/// \return
246249
/// A "StackFrame" JSON object with that follows the formal JSON
247250
/// definition outlined by Microsoft.
248-
llvm::json::Value CreateStackFrame(lldb::SBFrame &frame,
251+
llvm::json::Value CreateStackFrame(DAP &dap, lldb::SBFrame &frame,
249252
lldb::SBFormat &format);
250253

251254
/// Create a "StackFrame" label object for a LLDB thread.

lldb/tools/lldb-dap/Protocol/ProtocolRequests.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -468,7 +468,7 @@ struct SourceArguments {
468468
/// The reference to the source. This is the same as `source.sourceReference`.
469469
/// This is provided for backward compatibility since old clients do not
470470
/// understand the `source` attribute.
471-
int64_t sourceReference;
471+
int64_t sourceReference = LLDB_DAP_INVALID_SRC_REF;
472472
};
473473
bool fromJSON(const llvm::json::Value &, SourceArguments &, llvm::json::Path);
474474

lldb/tools/lldb-dap/Protocol/ProtocolTypes.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ llvm::json::Value toJSON(const Source &S) {
6565
result.insert({"name", *S.name});
6666
if (S.path)
6767
result.insert({"path", *S.path});
68-
if (S.sourceReference)
68+
if (S.sourceReference && (*S.sourceReference > LLDB_DAP_INVALID_SRC_REF))
6969
result.insert({"sourceReference", *S.sourceReference});
7070
if (S.presentationHint)
7171
result.insert({"presentationHint", *S.presentationHint});

lldb/tools/lldb-dap/Protocol/ProtocolTypes.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include <string>
2929

3030
#define LLDB_DAP_INVALID_VARRERF UINT64_MAX
31+
#define LLDB_DAP_INVALID_SRC_REF 0
3132

3233
namespace lldb_dap::protocol {
3334

@@ -328,7 +329,7 @@ struct Source {
328329
/// `source` request (even if a path is specified). Since a `sourceReference`
329330
/// is only valid for a session, it can not be used to persist a source. The
330331
/// value should be less than or equal to 2147483647 (2^31-1).
331-
std::optional<int64_t> sourceReference;
332+
std::optional<int32_t> sourceReference;
332333

333334
/// A hint for how to present the source in the UI. A value of `deemphasize`
334335
/// can be used to indicate that the source is not available or that it is

0 commit comments

Comments
 (0)