Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions lldb/include/lldb/API/SBTarget.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class LLDB_API SBTarget {
eBroadcastBitWatchpointChanged = (1 << 3),
eBroadcastBitSymbolsLoaded = (1 << 4),
eBroadcastBitSymbolsChanged = (1 << 5),
eBroadcastBitNewTargetCreated = (1 << 6),
};

// Constructors
Expand All @@ -64,6 +65,10 @@ class LLDB_API SBTarget {

static lldb::SBTarget GetTargetFromEvent(const lldb::SBEvent &event);

/// For eBroadcastBitNewTargetCreated events, returns the newly created
/// target. For other event types, returns an invalid SBTarget.
static lldb::SBTarget GetCreatedTargetFromEvent(const lldb::SBEvent &event);

static uint32_t GetNumModulesFromEvent(const lldb::SBEvent &event);

static lldb::SBModule GetModuleAtIndexFromEvent(const uint32_t idx,
Expand Down Expand Up @@ -365,6 +370,16 @@ class LLDB_API SBTarget {
/// LLDB_INVALID_GLOBALLY_UNIQUE_TARGET_ID if the target is invalid.
lldb::user_id_t GetGloballyUniqueID() const;

/// Get the target session name for this target.
///
/// The target session name provides a meaningful name for IDEs or tools to
/// display to help the user identify the origin and purpose of the target.
///
/// \return
/// The target session name for this target, or nullptr if the target is
/// invalid or has no target session name.
const char *GetTargetSessionName() const;

SBError SetLabel(const char *label);

/// Architecture opcode byte size width accessor
Expand Down
46 changes: 45 additions & 1 deletion lldb/include/lldb/Target/Target.h
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,7 @@ class Target : public std::enable_shared_from_this<Target>,
eBroadcastBitWatchpointChanged = (1 << 3),
eBroadcastBitSymbolsLoaded = (1 << 4),
eBroadcastBitSymbolsChanged = (1 << 5),
eBroadcastBitNewTargetCreated = (1 << 6),
};

// These two functions fill out the Broadcaster interface:
Expand All @@ -556,6 +557,13 @@ class Target : public std::enable_shared_from_this<Target>,
TargetEventData(const lldb::TargetSP &target_sp,
const ModuleList &module_list);

// Constructor for eBroadcastBitNewTargetCreated events. For this event
// type:
// - target_sp is the parent target (the subject/broadcaster of the event)
// - created_target_sp is the newly created target
TargetEventData(const lldb::TargetSP &target_sp,
const lldb::TargetSP &created_target_sp);

~TargetEventData() override;

static llvm::StringRef GetFlavorString();
Expand All @@ -570,14 +578,23 @@ class Target : public std::enable_shared_from_this<Target>,

static lldb::TargetSP GetTargetFromEvent(const Event *event_ptr);

// For eBroadcastBitNewTargetCreated events, returns the newly created
// target. For other event types, returns an invalid target.
static lldb::TargetSP GetCreatedTargetFromEvent(const Event *event_ptr);

static ModuleList GetModuleListFromEvent(const Event *event_ptr);

const lldb::TargetSP &GetTarget() const { return m_target_sp; }

const lldb::TargetSP &GetCreatedTarget() const {
return m_created_target_sp;
}

const ModuleList &GetModuleList() const { return m_module_list; }

private:
lldb::TargetSP m_target_sp;
lldb::TargetSP m_created_target_sp;
ModuleList m_module_list;

TargetEventData(const TargetEventData &) = delete;
Expand Down Expand Up @@ -622,6 +639,30 @@ class Target : public std::enable_shared_from_this<Target>,
/// requirements.
llvm::Error SetLabel(llvm::StringRef label);

/// Get the target session name for this target.
///
/// Provides a meaningful name for IDEs or tools to display for dynamically
/// created targets. Defaults to "Session {ID}" based on the globally unique
/// ID.
///
/// \return
/// The target session name for this target.
llvm::StringRef GetTargetSessionName() { return m_target_session_name; }

/// Set the target session name for this target.
///
/// This should typically be set along with the event
/// eBroadcastBitNewTargetCreated. Useful for scripts or triggers that
/// automatically create targets and want to provide meaningful names that
/// IDEs or other tools can display to help users identify the origin and
/// purpose of each target.
///
/// \param[in] target_session_name
/// The target session name to set for this target.
void SetTargetSessionName(llvm::StringRef target_session_name) {
m_target_session_name = target_session_name.str();
}

/// Find a binary on the system and return its Module,
/// or return an existing Module that is already in the Target.
///
Expand Down Expand Up @@ -1719,8 +1760,11 @@ class Target : public std::enable_shared_from_this<Target>,
bool m_is_dummy_target;
unsigned m_next_persistent_variable_index = 0;
lldb::user_id_t m_target_unique_id =
LLDB_INVALID_GLOBALLY_UNIQUE_TARGET_ID; /// The globally unique ID
LLDB_INVALID_GLOBALLY_UNIQUE_TARGET_ID; ///< The globally unique ID
/// assigned to this target
std::string m_target_session_name; ///< The target session name for this
/// target, used to name debugging
/// sessions in DAP.
/// An optional \a lldb_private::Trace object containing processor trace
/// information of this target.
lldb::TraceSP m_trace_sp;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -785,6 +785,8 @@ def request_attach(
*,
program: Optional[str] = None,
pid: Optional[int] = None,
debuggerId: Optional[int] = None,
targetId: Optional[int] = None,
waitFor=False,
initCommands: Optional[list[str]] = None,
preRunCommands: Optional[list[str]] = None,
Expand All @@ -804,6 +806,10 @@ def request_attach(
args_dict["pid"] = pid
if program is not None:
args_dict["program"] = program
if debuggerId is not None:
args_dict["debuggerId"] = debuggerId
if targetId is not None:
args_dict["targetId"] = targetId
if waitFor:
args_dict["waitFor"] = waitFor
args_dict["initCommands"] = self.init_commands
Expand Down
14 changes: 14 additions & 0 deletions lldb/source/API/SBTarget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,12 @@ SBTarget SBTarget::GetTargetFromEvent(const SBEvent &event) {
return Target::TargetEventData::GetTargetFromEvent(event.get());
}

SBTarget SBTarget::GetCreatedTargetFromEvent(const SBEvent &event) {
LLDB_INSTRUMENT_VA(event);

return Target::TargetEventData::GetCreatedTargetFromEvent(event.get());
}

uint32_t SBTarget::GetNumModulesFromEvent(const SBEvent &event) {
LLDB_INSTRUMENT_VA(event);

Expand Down Expand Up @@ -1641,6 +1647,14 @@ lldb::user_id_t SBTarget::GetGloballyUniqueID() const {
return LLDB_INVALID_GLOBALLY_UNIQUE_TARGET_ID;
}

const char *SBTarget::GetTargetSessionName() const {
LLDB_INSTRUMENT_VA(this);

if (TargetSP target_sp = GetSP())
return ConstString(target_sp->GetTargetSessionName()).AsCString();
return nullptr;
}

SBError SBTarget::SetLabel(const char *label) {
LLDB_INSTRUMENT_VA(this, label);

Expand Down
17 changes: 17 additions & 0 deletions lldb/source/Target/Target.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -185,13 +185,16 @@ Target::Target(Debugger &debugger, const ArchSpec &target_arch,
m_internal_stop_hooks(), m_latest_stop_hook_id(0), m_valid(true),
m_suppress_stop_hooks(false), m_is_dummy_target(is_dummy_target),
m_target_unique_id(g_target_unique_id++),
m_target_session_name(
llvm::formatv("Session {0}", m_target_unique_id).str()),
m_frame_recognizer_manager_up(
std::make_unique<StackFrameRecognizerManager>()) {
SetEventName(eBroadcastBitBreakpointChanged, "breakpoint-changed");
SetEventName(eBroadcastBitModulesLoaded, "modules-loaded");
SetEventName(eBroadcastBitModulesUnloaded, "modules-unloaded");
SetEventName(eBroadcastBitWatchpointChanged, "watchpoint-changed");
SetEventName(eBroadcastBitSymbolsLoaded, "symbols-loaded");
SetEventName(eBroadcastBitNewTargetCreated, "new-target-created");

CheckInWithManager();

Expand Down Expand Up @@ -5198,6 +5201,11 @@ Target::TargetEventData::TargetEventData(const lldb::TargetSP &target_sp,
const ModuleList &module_list)
: EventData(), m_target_sp(target_sp), m_module_list(module_list) {}

Target::TargetEventData::TargetEventData(
const lldb::TargetSP &target_sp, const lldb::TargetSP &created_target_sp)
: EventData(), m_target_sp(target_sp),
m_created_target_sp(created_target_sp), m_module_list() {}

Target::TargetEventData::~TargetEventData() = default;

llvm::StringRef Target::TargetEventData::GetFlavorString() {
Expand Down Expand Up @@ -5232,6 +5240,15 @@ TargetSP Target::TargetEventData::GetTargetFromEvent(const Event *event_ptr) {
return target_sp;
}

TargetSP
Target::TargetEventData::GetCreatedTargetFromEvent(const Event *event_ptr) {
TargetSP created_target_sp;
const TargetEventData *event_data = GetEventDataFromEvent(event_ptr);
if (event_data)
created_target_sp = event_data->m_created_target_sp;
return created_target_sp;
}

ModuleList
Target::TargetEventData::GetModuleListFromEvent(const Event *event_ptr) {
ModuleList module_list;
Expand Down
35 changes: 35 additions & 0 deletions lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,38 @@ def test_by_name_waitFor(self):
self.spawn_thread.start()
self.attach(program=program, waitFor=True)
self.continue_and_verify_pid()

def test_attach_with_missing_debuggerId_or_targetId(self):
"""
Test that attaching with only one of debuggerId/targetId specified
fails with the expected error message.
"""
self.build_and_create_debug_adapter()

# Test with only targetId specified (no debuggerId)
resp = self.attach(targetId=99999, expectFailure=True)
self.assertFalse(resp["success"])
self.assertIn(
"Both debuggerId and targetId must be specified together",
resp["body"]["error"]["format"],
)

def test_attach_with_invalid_debuggerId_and_targetId(self):
"""
Test that attaching with both debuggerId and targetId specified but
invalid fails with an appropriate error message.
"""
self.build_and_create_debug_adapter()

# Attach with both debuggerId=9999 and targetId=99999 (both invalid).
# Since debugger ID 9999 likely doesn't exist in the global registry,
# we expect a validation error.
resp = self.attach(debuggerId=9999, targetId=99999, expectFailure=True)
self.assertFalse(resp["success"])
error_msg = resp["body"]["error"]["format"]
# Either error is acceptable - both indicate the debugger reuse
# validation is working correctly
self.assertTrue(
"Unable to find existing debugger" in error_msg
or f"Expected debugger/target not found error, got: {error_msg}"
)
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,54 @@ def test_startDebugging(self):
request = self.dap_server.reverse_requests[0]
self.assertEqual(request["arguments"]["configuration"]["pid"], 321)
self.assertEqual(request["arguments"]["request"], "attach")

def test_startDebugging_debugger_reuse(self):
"""
Tests that debugger and target IDs can be passed through startDebugging
for debugger reuse. This verifies the infrastructure for child DAP
sessions to reuse the parent's debugger and attach to an existing target.
"""
program = self.getBuildArtifact("a.out")
source = "main.c"
self.build_and_launch(program)

breakpoint_line = line_number(source, "// breakpoint")
self.set_source_breakpoints(source, [breakpoint_line])
self.continue_to_next_stop()

# Use mock IDs to test the infrastructure
# In a real scenario, these would come from the parent session
test_debugger_id = 1
test_target_id = 100

# Send a startDebugging request with debuggerId and targetId
# This simulates creating a child DAP session that reuses the debugger
self.dap_server.request_evaluate(
f'`lldb-dap start-debugging attach \'{{"debuggerId":{test_debugger_id},"targetId":{test_target_id}}}\'',
context="repl",
)

self.continue_to_exit()

# Verify the reverse request was sent with the correct IDs
self.assertEqual(
len(self.dap_server.reverse_requests),
1,
"Should have received one startDebugging reverse request",
)

request = self.dap_server.reverse_requests[0]
self.assertEqual(request["command"], "startDebugging")
self.assertEqual(request["arguments"]["request"], "attach")

config = request["arguments"]["configuration"]
self.assertEqual(
config["debuggerId"],
test_debugger_id,
"Reverse request should include debugger ID",
)
self.assertEqual(
config["targetId"],
test_target_id,
"Reverse request should include target ID",
)
1 change: 1 addition & 0 deletions lldb/tools/lldb-dap/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ add_lldb_library(lldbDAP
DAP.cpp
DAPError.cpp
DAPLog.cpp
DAPSessionManager.cpp
EventHelper.cpp
ExceptionBreakpoint.cpp
FifoFiles.cpp
Expand Down
Loading