Skip to content

Commit dea75d7

Browse files
committed
Use StoreTargetById/RetrieveTargetById and add more comments
1 parent 7ed6442 commit dea75d7

File tree

5 files changed

+109
-65
lines changed

5 files changed

+109
-65
lines changed

lldb/test/API/tools/lldb-dap/attach/TestDAP_attach.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,5 +83,5 @@ def test_attach_with_invalid_targetId(self):
8383
resp = self.attach(targetId=99999, expectFailure=True)
8484
self.assertFalse(resp["success"])
8585
self.assertIn(
86-
"Unable to find existing debugger", resp["body"]["error"]["format"]
86+
"Unable to find existing target", resp["body"]["error"]["format"]
8787
)

lldb/tools/lldb-dap/DAP.cpp

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1312,14 +1312,15 @@ void DAP::StartEventThreads() {
13121312
llvm::Error DAP::InitializeDebugger(std::optional<uint32_t> target_id) {
13131313
// Initialize debugger instance (shared or individual).
13141314
if (target_id) {
1315-
std::optional<lldb::SBDebugger> shared_debugger =
1316-
DAPSessionManager::GetInstance().GetSharedDebugger(*target_id);
1317-
// If the target ID is not valid, then we won't find a debugger.
1318-
if (!shared_debugger) {
1315+
std::optional<lldb::SBTarget> shared_target =
1316+
DAPSessionManager::GetInstance().TakeTargetById(*target_id);
1317+
// If the target ID is not valid, then we won't find a target.
1318+
if (!shared_target) {
13191319
return llvm::createStringError(
1320-
"Unable to find existing debugger for target ID");
1320+
"Unable to find existing target for target ID");
13211321
}
1322-
debugger = shared_debugger.value();
1322+
// Get the debugger from the target and set it up.
1323+
debugger = shared_target->GetDebugger();
13231324
StartEventThreads();
13241325
return llvm::Error::success();
13251326
}
@@ -1567,10 +1568,9 @@ void DAP::EventThread() {
15671568
} else if (event_mask & lldb::SBTarget::eBroadcastBitNewTargetCreated) {
15681569
auto target = lldb::SBTarget::GetTargetFromEvent(event);
15691570

1570-
// Generate unique target ID and set the shared debugger.
1571+
// Generate unique target ID and store the target for handoff.
15711572
uint32_t target_id = target.GetGloballyUniqueID();
1572-
DAPSessionManager::GetInstance().SetSharedDebugger(target_id,
1573-
debugger);
1573+
DAPSessionManager::GetInstance().StoreTargetById(target_id, target);
15741574

15751575
// We create an attach config that will select the unique
15761576
// target ID of the created target. The DAP instance will attach to

lldb/tools/lldb-dap/DAPSessionManager.cpp

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,10 @@ ManagedEventThread::~ManagedEventThread() {
3131
}
3232

3333
DAPSessionManager &DAPSessionManager::GetInstance() {
34-
// NOTE: intentional leak to avoid issues with C++ destructor chain
35-
// Use std::call_once for thread-safe initialization.
3634
static std::once_flag initialized;
37-
static DAPSessionManager *instance = nullptr;
35+
static DAPSessionManager *instance =
36+
nullptr; // NOTE: intentional leak to avoid issues with C++ destructor
37+
// chain
3838

3939
std::call_once(initialized, []() { instance = new DAPSessionManager(); });
4040

@@ -104,7 +104,7 @@ DAPSessionManager::GetEventThreadForDebugger(lldb::SBDebugger debugger,
104104
// Try to use shared event thread, if it exists.
105105
if (auto it = m_debugger_event_threads.find(debugger_id);
106106
it != m_debugger_event_threads.end()) {
107-
if (auto thread_sp = it->second.lock())
107+
if (std::shared_ptr<ManagedEventThread> thread_sp = it->second.lock())
108108
return thread_sp;
109109
// Our weak pointer has expired.
110110
m_debugger_event_threads.erase(it);
@@ -118,21 +118,21 @@ DAPSessionManager::GetEventThreadForDebugger(lldb::SBDebugger debugger,
118118
return new_thread_sp;
119119
}
120120

121-
void DAPSessionManager::SetSharedDebugger(uint32_t target_id,
122-
lldb::SBDebugger debugger) {
121+
void DAPSessionManager::StoreTargetById(uint32_t target_id,
122+
lldb::SBTarget target) {
123123
std::lock_guard<std::mutex> lock(m_sessions_mutex);
124-
m_target_to_debugger_map[target_id] = debugger;
124+
m_target_map[target_id] = target;
125125
}
126126

127-
std::optional<lldb::SBDebugger>
128-
DAPSessionManager::GetSharedDebugger(uint32_t target_id) {
127+
std::optional<lldb::SBTarget>
128+
DAPSessionManager::TakeTargetById(uint32_t target_id) {
129129
std::lock_guard<std::mutex> lock(m_sessions_mutex);
130-
auto pos = m_target_to_debugger_map.find(target_id);
131-
if (pos == m_target_to_debugger_map.end())
130+
auto pos = m_target_map.find(target_id);
131+
if (pos == m_target_map.end())
132132
return std::nullopt;
133-
lldb::SBDebugger debugger = pos->second;
134-
m_target_to_debugger_map.erase(pos);
135-
return debugger;
133+
lldb::SBTarget target = pos->second;
134+
m_target_map.erase(pos);
135+
return target;
136136
}
137137

138138
DAP *DAPSessionManager::FindDAPForTarget(lldb::SBTarget target) {
@@ -146,9 +146,10 @@ DAP *DAPSessionManager::FindDAPForTarget(lldb::SBTarget target) {
146146
}
147147

148148
void DAPSessionManager::CleanupSharedResources() {
149-
// SBDebugger destructors will handle cleanup when the map entries are
149+
// Note: Caller must hold m_sessions_mutex.
150+
// SBTarget destructors will handle cleanup when the map entries are
150151
// destroyed.
151-
m_target_to_debugger_map.clear();
152+
m_target_map.clear();
152153
}
153154

154155
void DAPSessionManager::ReleaseExpiredEventThreads() {

lldb/tools/lldb-dap/DAPSessionManager.h

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,10 @@ class ManagedEventThread {
4343
std::thread m_event_thread;
4444
};
4545

46-
/// Global DAP session manager.
46+
/// Global DAP session manager that manages multiple concurrent DAP sessions in
47+
/// a single lldb-dap process. Handles session lifecycle tracking, coordinates
48+
/// shared debugger event threads, and facilitates target handoff between
49+
/// sessions for dynamically created targets.
4750
class DAPSessionManager {
4851
public:
4952
/// Get the singleton instance of the DAP session manager.
@@ -52,24 +55,27 @@ class DAPSessionManager {
5255
/// Register a DAP session.
5356
void RegisterSession(lldb_private::MainLoop *loop, DAP *dap);
5457

55-
/// Unregister a DAP session.
58+
/// Unregister a DAP session. Called by sessions when they complete their
59+
/// disconnection, which unblocks WaitForAllSessionsToDisconnect().
5660
void UnregisterSession(lldb_private::MainLoop *loop);
5761

5862
/// Get all active DAP sessions.
5963
std::vector<DAP *> GetActiveSessions();
6064

61-
/// Disconnect all active sessions.
65+
/// Disconnect all registered sessions by calling Disconnect() on
66+
/// each and requesting their event loops to terminate. Used during
67+
/// shutdown to force all sessions to begin disconnecting.
6268
void DisconnectAllSessions();
6369

64-
/// Wait for all sessions to finish disconnecting.
65-
/// Returns an error if any client disconnection failed, otherwise success.
70+
/// Block until all sessions disconnect and unregister. Returns an error if
71+
/// DisconnectAllSessions() was called and any disconnection failed.
6672
llvm::Error WaitForAllSessionsToDisconnect();
6773

68-
/// Set the shared debugger instance for a unique target ID.
69-
void SetSharedDebugger(uint32_t target_id, lldb::SBDebugger debugger);
74+
/// Store a target for later retrieval by another session.
75+
void StoreTargetById(uint32_t target_id, lldb::SBTarget target);
7076

71-
/// Get the shared debugger instance for a unique target ID.
72-
std::optional<lldb::SBDebugger> GetSharedDebugger(uint32_t target_id);
77+
/// Retrieve and remove a stored target by its globally unique target ID.
78+
std::optional<lldb::SBTarget> TakeTargetById(uint32_t target_id);
7379

7480
/// Get or create event thread for a specific debugger.
7581
std::shared_ptr<ManagedEventThread>
@@ -104,12 +110,12 @@ class DAPSessionManager {
104110
std::condition_variable m_sessions_condition;
105111
std::map<lldb_private::MainLoop *, DAP *> m_active_sessions;
106112

107-
/// Optional map from target ID to shared debugger set when the native
108-
/// process creates a new target.
109-
std::map<uint32_t, lldb::SBDebugger> m_target_to_debugger_map;
113+
/// Map from target ID to target for handing off newly created targets
114+
/// between sessions.
115+
std::map<uint32_t, lldb::SBTarget> m_target_map;
110116

111-
/// Map from debugger ID to its event thread used for when
112-
/// multiple DAP sessions are using the same debugger instance.
117+
/// Map from debugger ID to its event thread, used when multiple DAP sessions
118+
/// share the same debugger instance.
113119
std::map<lldb::user_id_t, std::weak_ptr<ManagedEventThread>>
114120
m_debugger_event_threads;
115121
};

lldb/unittests/DAP/DAPSessionManagerTest.cpp

Lines changed: 62 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -25,58 +25,77 @@ TEST_F(DAPSessionManagerTest, GetInstanceReturnsSameSingleton) {
2525
EXPECT_EQ(&instance1, &instance2);
2626
}
2727

28-
TEST_F(DAPSessionManagerTest, SharedDebuggerSetAndGet) {
28+
TEST_F(DAPSessionManagerTest, StoreAndTakeTarget) {
2929
DAPSessionManager &manager = DAPSessionManager::GetInstance();
3030

3131
SBDebugger debugger = SBDebugger::Create();
3232
ASSERT_TRUE(debugger.IsValid());
33+
// Create an empty target (no executable) since we're only testing session
34+
// management functionality, not actual debugging operations.
35+
SBTarget target = debugger.CreateTarget("");
36+
ASSERT_TRUE(target.IsValid());
3337

34-
uint32_t target_id = 12345;
35-
manager.SetSharedDebugger(target_id, debugger);
38+
uint32_t target_id = target.GetGloballyUniqueID();
39+
manager.StoreTargetById(target_id, target);
3640

37-
// Retrieval consumes the debugger (removes from map).
38-
std::optional<SBDebugger> retrieved = manager.GetSharedDebugger(target_id);
41+
// Retrieval consumes the target (removes from map).
42+
std::optional<SBTarget> retrieved = manager.TakeTargetById(target_id);
3943
ASSERT_TRUE(retrieved.has_value());
4044
EXPECT_TRUE(retrieved->IsValid());
41-
EXPECT_EQ(retrieved->GetID(), debugger.GetID());
45+
// Verify we got the correct target back.
46+
EXPECT_EQ(retrieved->GetDebugger().GetID(), debugger.GetID());
47+
EXPECT_EQ(*retrieved, target);
4248

43-
// Second retrieval should fail.
44-
std::optional<SBDebugger> second_retrieval =
45-
manager.GetSharedDebugger(target_id);
49+
// Second retrieval should fail (one-time retrieval semantics).
50+
std::optional<SBTarget> second_retrieval = manager.TakeTargetById(target_id);
4651
EXPECT_FALSE(second_retrieval.has_value());
4752

4853
SBDebugger::Destroy(debugger);
4954
}
5055

51-
TEST_F(DAPSessionManagerTest, GetSharedDebuggerWithInvalidId) {
56+
TEST_F(DAPSessionManagerTest, GetTargetWithInvalidId) {
5257
DAPSessionManager &manager = DAPSessionManager::GetInstance();
5358

54-
std::optional<SBDebugger> result = manager.GetSharedDebugger(99999);
59+
std::optional<SBTarget> result = manager.TakeTargetById(99999);
5560

5661
EXPECT_FALSE(result.has_value());
5762
}
5863

59-
TEST_F(DAPSessionManagerTest, MultipleSharedDebuggersWithDifferentIds) {
64+
TEST_F(DAPSessionManagerTest, MultipleTargetsWithDifferentIds) {
6065
DAPSessionManager &manager = DAPSessionManager::GetInstance();
6166

6267
SBDebugger debugger1 = SBDebugger::Create();
6368
SBDebugger debugger2 = SBDebugger::Create();
6469
ASSERT_TRUE(debugger1.IsValid());
6570
ASSERT_TRUE(debugger2.IsValid());
71+
// Create empty targets (no executable) since we're only testing session
72+
// management functionality, not actual debugging operations.
73+
SBTarget target1 = debugger1.CreateTarget("");
74+
SBTarget target2 = debugger2.CreateTarget("");
75+
ASSERT_TRUE(target1.IsValid());
76+
ASSERT_TRUE(target2.IsValid());
6677

67-
uint32_t target_id_1 = 1001;
68-
uint32_t target_id_2 = 1002;
78+
uint32_t target_id_1 = target1.GetGloballyUniqueID();
79+
uint32_t target_id_2 = target2.GetGloballyUniqueID();
6980

70-
manager.SetSharedDebugger(target_id_1, debugger1);
71-
manager.SetSharedDebugger(target_id_2, debugger2);
81+
// Sanity check the targets have distinct IDs.
82+
EXPECT_NE(target_id_1, target_id_2);
7283

73-
std::optional<SBDebugger> retrieved1 = manager.GetSharedDebugger(target_id_1);
84+
manager.StoreTargetById(target_id_1, target1);
85+
manager.StoreTargetById(target_id_2, target2);
86+
87+
std::optional<SBTarget> retrieved1 = manager.TakeTargetById(target_id_1);
7488
ASSERT_TRUE(retrieved1.has_value());
75-
EXPECT_EQ(retrieved1->GetID(), debugger1.GetID());
89+
EXPECT_TRUE(retrieved1->IsValid());
90+
// Verify we got the correct target by comparing debugger and target IDs.
91+
EXPECT_EQ(retrieved1->GetDebugger().GetID(), debugger1.GetID());
92+
EXPECT_EQ(*retrieved1, target1);
7693

77-
std::optional<SBDebugger> retrieved2 = manager.GetSharedDebugger(target_id_2);
94+
std::optional<SBTarget> retrieved2 = manager.TakeTargetById(target_id_2);
7895
ASSERT_TRUE(retrieved2.has_value());
79-
EXPECT_EQ(retrieved2->GetID(), debugger2.GetID());
96+
EXPECT_TRUE(retrieved2->IsValid());
97+
EXPECT_EQ(retrieved2->GetDebugger().GetID(), debugger2.GetID());
98+
EXPECT_EQ(*retrieved2, target2);
8099

81100
SBDebugger::Destroy(debugger1);
82101
SBDebugger::Destroy(debugger2);
@@ -87,14 +106,32 @@ TEST_F(DAPSessionManagerTest, CleanupSharedResources) {
87106

88107
SBDebugger debugger = SBDebugger::Create();
89108
ASSERT_TRUE(debugger.IsValid());
90-
manager.SetSharedDebugger(5555, debugger);
91-
std::optional<SBDebugger> initial_result = manager.GetSharedDebugger(5555);
92-
ASSERT_TRUE(initial_result.has_value());
109+
// Create empty targets (no executable) since we're only testing session
110+
// management functionality, not actual debugging operations.
111+
SBTarget target1 = debugger.CreateTarget("");
112+
SBTarget target2 = debugger.CreateTarget("");
113+
ASSERT_TRUE(target1.IsValid());
114+
ASSERT_TRUE(target2.IsValid());
115+
116+
uint32_t target_id_1 = target1.GetGloballyUniqueID();
117+
uint32_t target_id_2 = target2.GetGloballyUniqueID();
118+
119+
// Sanity check the targets have distinct IDs.
120+
EXPECT_NE(target_id_1, target_id_2);
93121

122+
// Store multiple targets to verify cleanup removes them all.
123+
manager.StoreTargetById(target_id_1, target1);
124+
manager.StoreTargetById(target_id_2, target2);
125+
126+
// Cleanup should remove all stored targets.
94127
manager.CleanupSharedResources();
95128

96-
std::optional<SBDebugger> result = manager.GetSharedDebugger(5555);
97-
EXPECT_FALSE(result.has_value());
129+
// Verify both targets are gone after cleanup.
130+
std::optional<SBTarget> result1 = manager.TakeTargetById(target_id_1);
131+
EXPECT_FALSE(result1.has_value());
132+
133+
std::optional<SBTarget> result2 = manager.TakeTargetById(target_id_2);
134+
EXPECT_FALSE(result2.has_value());
98135

99136
SBDebugger::Destroy(debugger);
100137
}

0 commit comments

Comments
 (0)