-
Notifications
You must be signed in to change notification settings - Fork 15.1k
[lldb-dap] assembly breakpoints #139969
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[lldb-dap] assembly breakpoints #139969
Changes from 15 commits
d6325b3
ee49203
dbb2f9b
3699524
61623de
f5fd76d
40f68e4
199512f
dbad33b
2e9edca
0515c6d
9dbca55
396970d
7a0e0ee
71dce6c
15e7294
044fd90
ef75e4d
f9d9024
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| C_SOURCES := main.c | ||
|
|
||
| include Makefile.rules |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| """ | ||
| Test lldb-dap setBreakpoints request | ||
|
||
| """ | ||
|
|
||
|
|
||
| import dap_server | ||
| import shutil | ||
| from lldbsuite.test.decorators import * | ||
| from lldbsuite.test.lldbtest import line_number | ||
| from lldbsuite.test import lldbutil | ||
| import lldbdap_testcase | ||
| import os | ||
|
|
||
|
|
||
| class TestDAP_setBreakpointsAssembly(lldbdap_testcase.DAPTestCaseBase): | ||
| # @skipIfWindows | ||
| def test_functionality(self): | ||
|
||
| """Tests hitting assembly source breakpoints""" | ||
| program = self.getBuildArtifact("a.out") | ||
| self.build_and_launch(program) | ||
|
|
||
| self.dap_server.request_evaluate( | ||
| "`settings set stop-disassembly-display no-debuginfo", context="repl" | ||
| ) | ||
|
|
||
| finish_line = line_number("main.c", "// Break here") | ||
| finish_breakpoints = self.set_source_breakpoints("main.c", [finish_line]) | ||
|
|
||
| assmebly_func_breakpoints = self.set_function_breakpoints(["assembly_func"]) | ||
| self.continue_to_breakpoints(assmebly_func_breakpoints) | ||
|
|
||
| assembly_func_frame = self.get_stackFrames()[0] | ||
| self.assertIn( | ||
| "sourceReference", | ||
| assembly_func_frame.get("source"), | ||
| "Expected assembly source frame", | ||
| ) | ||
|
|
||
| line = assembly_func_frame["line"] | ||
|
|
||
| # Set an assembly breakpoint in the next line and check that it's hit | ||
| source_reference = assembly_func_frame["source"]["sourceReference"] | ||
| assembly_breakpoint_ids = self.set_source_breakpoints_assembly( | ||
| source_reference, [line + 1] | ||
| ) | ||
| self.continue_to_breakpoints(assembly_breakpoint_ids) | ||
|
|
||
| # Continue again and verify it hits in the next function call | ||
| self.continue_to_breakpoints(assmebly_func_breakpoints) | ||
| self.continue_to_breakpoints(assembly_breakpoint_ids) | ||
|
|
||
| # Clear the breakpoint and then check that the assembly breakpoint does not hit next time | ||
| self.set_source_breakpoints_assembly(source_reference, []) | ||
| self.continue_to_breakpoints(assmebly_func_breakpoints) | ||
| self.continue_to_breakpoints(finish_breakpoints) | ||
da-viper marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| #include <stddef.h> | ||
eronnen marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| __attribute__((nodebug)) int assembly_func(int n) { | ||
| n += 1; | ||
| n += 2; | ||
| n += 3; | ||
|
|
||
| return n; | ||
| } | ||
|
|
||
| int main(int argc, char const *argv[]) { | ||
| assembly_func(10); | ||
| assembly_func(20); | ||
| assembly_func(30); | ||
| return 0; // Break here | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -9,10 +9,12 @@ | |||||
| #include "Breakpoint.h" | ||||||
| #include "DAP.h" | ||||||
| #include "JSONUtils.h" | ||||||
| #include "LLDBUtils.h" | ||||||
| #include "lldb/API/SBAddress.h" | ||||||
| #include "lldb/API/SBBreakpointLocation.h" | ||||||
| #include "lldb/API/SBLineEntry.h" | ||||||
| #include "lldb/API/SBMutex.h" | ||||||
| #include "lldb/lldb-enumerations.h" | ||||||
| #include "llvm/ADT/StringExtras.h" | ||||||
| #include <cstddef> | ||||||
| #include <cstdint> | ||||||
|
|
@@ -63,14 +65,31 @@ protocol::Breakpoint Breakpoint::ToProtocolBreakpoint() { | |||||
| std::string formatted_addr = | ||||||
| "0x" + llvm::utohexstr(bp_addr.GetLoadAddress(m_bp.GetTarget())); | ||||||
| breakpoint.instructionReference = formatted_addr; | ||||||
|
|
||||||
| lldb::StopDisassemblyType stop_disassembly_display = | ||||||
| GetStopDisassemblyDisplay(m_dap.debugger); | ||||||
| auto line_entry = bp_addr.GetLineEntry(); | ||||||
| const auto line = line_entry.GetLine(); | ||||||
| if (line != UINT32_MAX) | ||||||
| breakpoint.line = line; | ||||||
| const auto column = line_entry.GetColumn(); | ||||||
| if (column != 0) | ||||||
| breakpoint.column = column; | ||||||
| breakpoint.source = CreateSource(line_entry); | ||||||
| if (!ShouldDisplayAssemblySource(line_entry, stop_disassembly_display)) { | ||||||
| const auto line = line_entry.GetLine(); | ||||||
| if (line != LLDB_INVALID_LINE_NUMBER) | ||||||
| breakpoint.line = line; | ||||||
| const auto column = line_entry.GetColumn(); | ||||||
| if (column != LLDB_INVALID_COLUMN_NUMBER) | ||||||
| breakpoint.column = column; | ||||||
| breakpoint.source = CreateSource(line_entry); | ||||||
| } else { | ||||||
| // Breakpoint made by assembly | ||||||
|
||||||
| // Breakpoint made by assembly | |
| // Assembly breakpoint. |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -1620,6 +1620,81 @@ void DAP::EventThread() { | |||||
| } | ||||||
| } | ||||||
|
|
||||||
| std::vector<protocol::Breakpoint> DAP::SetSourceBreakpoints( | ||||||
| const protocol::Source &source, | ||||||
| const std::optional<std::vector<protocol::SourceBreakpoint>> &breakpoints) { | ||||||
| std::vector<protocol::Breakpoint> response_breakpoints; | ||||||
| if (source.sourceReference) { | ||||||
| // breakpoint set by assembly source. | ||||||
|
||||||
| // breakpoint set by assembly source. | |
| // Breakpoint set by assembly source. |
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| // breakpoint set by a regular source file. | |
| // Breakpoint set by a regular source file. |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -60,7 +60,7 @@ | |||||
|
|
||||||
| namespace lldb_dap { | ||||||
|
|
||||||
| typedef llvm::DenseMap<std::pair<uint32_t, uint32_t>, SourceBreakpoint> | ||||||
| typedef std::map<std::pair<uint32_t, uint32_t>, SourceBreakpoint> | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. changed to |
||||||
| SourceBreakpointMap; | ||||||
| typedef llvm::StringMap<FunctionBreakpoint> FunctionBreakpointMap; | ||||||
| typedef llvm::DenseMap<lldb::addr_t, InstructionBreakpoint> | ||||||
|
|
@@ -168,7 +168,6 @@ struct DAP { | |||||
| lldb::SBTarget target; | ||||||
| Variables variables; | ||||||
| lldb::SBBroadcaster broadcaster; | ||||||
| llvm::StringMap<SourceBreakpointMap> source_breakpoints; | ||||||
| FunctionBreakpointMap function_breakpoints; | ||||||
| InstructionBreakpointMap instruction_breakpoints; | ||||||
| std::optional<std::vector<ExceptionBreakpoint>> exception_breakpoints; | ||||||
|
|
@@ -219,6 +218,9 @@ struct DAP { | |||||
| llvm::StringSet<> modules; | ||||||
| /// @} | ||||||
|
|
||||||
| /// Number of lines of assembly code to show when no debug info is available. | ||||||
| static constexpr uint32_t number_of_assembly_lines_for_nodebug = 32; | ||||||
|
||||||
| static constexpr uint32_t number_of_assembly_lines_for_nodebug = 32; | |
| static constexpr uint32_t k_number_of_assembly_lines_for_nodebug = 32; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we made protocol::Source work in a std::map (or llvm::DenseMap) as a key, could we merge these? That could simplify things and make this more consistent between the two.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it should be pretty simplified already since the only access to these maps is abstracted by DAP::SetSourceBreakpoints
It should be possible to use Source as a key but I think it might introduce new complications like implementing a hash / ordering for the struct, and there are extra fields in Source like name and presentationHint
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we keep the
request_*names just the DAP requests and make a helper without therequest_*prefix for this? I like the consistency of havingrequest_*being able to map to specific request and I think its valuable to keep that.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💯