Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 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
3 changes: 3 additions & 0 deletions lldb/include/lldb/API/SBDebugger.h
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,9 @@ class LLDB_API SBDebugger {
lldb::SBTarget FindTargetWithFileAndArch(const char *filename,
const char *arch);

/// Find a target with the specified unique ID
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Find a target with the specified unique ID
/// Find a target with the specified unique ID.

lldb::SBTarget FindTargetByGloballyUniqueID(lldb::user_id_t id);

/// Get the number of targets in the debugger.
uint32_t GetNumTargets();

Expand Down
8 changes: 8 additions & 0 deletions lldb/include/lldb/API/SBTarget.h
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,14 @@ class LLDB_API SBTarget {

const char *GetLabel() const;

/// Get the globally unique ID for this target. This ID is unique
/// across all debugger instances within the same lldb process.
///
/// \return
/// The globally unique ID for this target, or
/// LLDB_INVALID_GLOBALLY_UNIQUE_TARGET_ID if the target is invalid.
lldb::user_id_t GetGloballyUniqueID() const;

SBError SetLabel(const char *label);

/// Architecture opcode byte size width accessor
Expand Down
14 changes: 14 additions & 0 deletions lldb/include/lldb/Target/Target.h
Original file line number Diff line number Diff line change
Expand Up @@ -600,6 +600,17 @@ class Target : public std::enable_shared_from_this<Target>,

bool IsDummyTarget() const { return m_is_dummy_target; }

/// Get the globally unique ID for this target.
///
/// This ID is unique across all debugger instances and all targets,
/// within the same lldb process. The ID is assigned
/// during target construction and remains constant for the target's lifetime.
/// The first target created (typically the dummy target) gets ID 1.
///
/// \return
/// The globally unique ID for this target.
lldb::user_id_t GetGloballyUniqueID() const { return m_target_unique_id; }

const std::string &GetLabel() const { return m_label; }

/// Set a label for a target.
Expand Down Expand Up @@ -1651,6 +1662,9 @@ class Target : public std::enable_shared_from_this<Target>,
bool m_suppress_stop_hooks; /// Used to not run stop hooks for expressions
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
/// assigned to this target
/// An optional \a lldb_private::Trace object containing processor trace
/// information of this target.
lldb::TraceSP m_trace_sp;
Expand Down
11 changes: 11 additions & 0 deletions lldb/include/lldb/Target/TargetList.h
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,17 @@ class TargetList : public Broadcaster {

lldb::TargetSP FindTargetWithProcess(lldb_private::Process *process) const;

/// Find the target that has a globally unique ID that matches ID \a id
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Find the target that has a globally unique ID that matches ID \a id
/// Find the target that has a globally unique ID that matches ID \a id.

///
/// \param[in] id
/// The globally unique target ID to search our target list for.
///
/// \return
/// A shared pointer to a target object. The returned shared
/// pointer will contain nullptr if no target objects has a
/// matching target ID.
lldb::TargetSP FindTargetByGloballyUniqueID(lldb::user_id_t id) const;

lldb::TargetSP GetTargetSP(Target *target) const;

/// Send an async interrupt to one or all processes.
Expand Down
1 change: 1 addition & 0 deletions lldb/include/lldb/lldb-defines.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
#define LLDB_INVALID_QUEUE_ID 0
#define LLDB_INVALID_CPU_ID UINT32_MAX
#define LLDB_INVALID_WATCHPOINT_RESOURCE_ID UINT32_MAX
#define LLDB_INVALID_GLOBALLY_UNIQUE_TARGET_ID 0

/// CPU Type definitions
#define LLDB_ARCH_DEFAULT "systemArch"
Expand Down
11 changes: 11 additions & 0 deletions lldb/source/API/SBDebugger.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -983,6 +983,17 @@ uint32_t SBDebugger::GetIndexOfTarget(lldb::SBTarget target) {
return m_opaque_sp->GetTargetList().GetIndexOfTarget(target.GetSP());
}

SBTarget SBDebugger::FindTargetByGloballyUniqueID(lldb::user_id_t id) {
LLDB_INSTRUMENT_VA(this, id);
SBTarget sb_target;
if (m_opaque_sp) {
// No need to lock, the target list is thread safe
sb_target.SetSP(
m_opaque_sp->GetTargetList().FindTargetByGloballyUniqueID(id));
}
return sb_target;
}

SBTarget SBDebugger::FindTargetWithProcessID(lldb::pid_t pid) {
LLDB_INSTRUMENT_VA(this, pid);

Expand Down
8 changes: 8 additions & 0 deletions lldb/source/API/SBTarget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1632,6 +1632,14 @@ const char *SBTarget::GetLabel() const {
return nullptr;
}

lldb::user_id_t SBTarget::GetGloballyUniqueID() const {
LLDB_INSTRUMENT_VA(this);

if (TargetSP target_sp = GetSP())
return target_sp->GetGloballyUniqueID();
return LLDB_INVALID_GLOBALLY_UNIQUE_TARGET_ID;
}

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

Expand Down
3 changes: 3 additions & 0 deletions lldb/source/Target/Target.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ struct MainExecutableInstaller {
};
} // namespace

static std::atomic<lldb::user_id_t> g_target_unique_id{1};

template <typename Installer>
static Status installExecutable(const Installer &installer) {
if (!installer.m_local_file || !installer.m_remote_file)
Expand Down Expand Up @@ -183,6 +185,7 @@ Target::Target(Debugger &debugger, const ArchSpec &target_arch,
m_source_manager_up(), m_stop_hooks(), m_stop_hook_next_id(0),
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_frame_recognizer_manager_up(
std::make_unique<StackFrameRecognizerManager>()) {
SetEventName(eBroadcastBitBreakpointChanged, "breakpoint-changed");
Expand Down
12 changes: 12 additions & 0 deletions lldb/source/Target/TargetList.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,18 @@ TargetSP TargetList::FindTargetWithProcess(Process *process) const {
return target_sp;
}

TargetSP TargetList::FindTargetByGloballyUniqueID(lldb::user_id_t id) const {
std::lock_guard<std::recursive_mutex> guard(m_target_list_mutex);
auto it = llvm::find_if(m_target_list, [id](const TargetSP &item) {
return item->GetGloballyUniqueID() == id;
});

if (it != m_target_list.end())
return *it;

return TargetSP();
}

TargetSP TargetList::GetTargetSP(Target *target) const {
TargetSP target_sp;
if (!target)
Expand Down
147 changes: 147 additions & 0 deletions lldb/test/API/python_api/debugger/TestDebuggerAPI.py
Original file line number Diff line number Diff line change
Expand Up @@ -294,3 +294,150 @@ def test_version(self):

self.assertEqual(instance_str, class_str)
self.assertEqual(class_str, property_str)

def test_find_target_with_unique_id(self):
"""Test SBDebugger.FindTargetByGloballyUniqueID() functionality."""

# Test with invalid ID - should return invalid target
invalid_target = self.dbg.FindTargetByGloballyUniqueID(999999)
self.assertFalse(invalid_target.IsValid())

# Test with ID 0 - should return invalid target
zero_target = self.dbg.FindTargetByGloballyUniqueID(0)
self.assertFalse(zero_target.IsValid())

# Build a real executable and create target with it
self.build()
exe = self.getBuildArtifact("a.out")
target = self.dbg.CreateTarget(exe)
self.assertTrue(target.IsValid())

# Find the target using its unique ID
unique_id = target.GetGloballyUniqueID()
self.assertNotEqual(unique_id, lldb.LLDB_INVALID_GLOBALLY_UNIQUE_TARGET_ID)
found_target = self.dbg.FindTargetByGloballyUniqueID(unique_id)
self.assertTrue(found_target.IsValid())
self.assertEqual(
self.dbg.GetIndexOfTarget(target), self.dbg.GetIndexOfTarget(found_target)
)
self.assertEqual(found_target.GetGloballyUniqueID(), unique_id)

def test_target_unique_id_uniqueness(self):
"""Test that Target.GetGloballyUniqueID() returns unique values across multiple targets."""

# Create multiple targets and verify they all have unique IDs
self.build()
exe = self.getBuildArtifact("a.out")
targets = []
unique_ids = set()

for i in range(10):
target = self.dbg.CreateTarget(exe)
self.assertTrue(target.IsValid())

unique_id = target.GetGloballyUniqueID()
self.assertNotEqual(unique_id, 0)

# Verify this ID hasn't been used before
self.assertNotIn(
unique_id, unique_ids, f"Duplicate unique ID found: {unique_id}"
)

unique_ids.add(unique_id)
targets.append(target)

# Verify all targets can still be found by their IDs
for target in targets:
unique_id = target.GetGloballyUniqueID()
found = self.dbg.FindTargetByGloballyUniqueID(unique_id)
self.assertTrue(found.IsValid())
self.assertEqual(found.GetGloballyUniqueID(), unique_id)

def test_target_unique_id_uniqueness_after_deletion(self):
"""Test finding targets have unique ID after target deletion."""
# Create two targets
self.build()
exe = self.getBuildArtifact("a.out")
target1 = self.dbg.CreateTarget(exe)
target2 = self.dbg.CreateTarget(exe)
self.assertTrue(target1.IsValid())
self.assertTrue(target2.IsValid())

unique_id1 = target1.GetGloballyUniqueID()
unique_id2 = target2.GetGloballyUniqueID()
self.assertNotEqual(unique_id1, 0)
self.assertNotEqual(unique_id2, 0)
self.assertNotEqual(unique_id1, unique_id2)

# Verify we can find them initially
found_target1 = self.dbg.FindTargetByGloballyUniqueID(unique_id1)
found_target2 = self.dbg.FindTargetByGloballyUniqueID(unique_id2)
self.assertTrue(found_target1.IsValid())
self.assertTrue(found_target2.IsValid())
target2_index = self.dbg.GetIndexOfTarget(target2)

# Delete target 2
deleted = self.dbg.DeleteTarget(target2)
self.assertTrue(deleted)

# Try to find the deleted target - should not be found
not_found_target = self.dbg.FindTargetByGloballyUniqueID(unique_id2)
self.assertFalse(not_found_target.IsValid())

# Create a new target
target3 = self.dbg.CreateTarget(exe)
self.assertTrue(target3.IsValid())
# Target list index of target3 should be the same as target2's
# since it was deleted, but it should have a distinct unique ID
target3_index = self.dbg.GetIndexOfTarget(target3)
unique_id3 = target3.GetGloballyUniqueID()
self.assertEqual(target3_index, target2_index)
self.assertNotEqual(unique_id3, unique_id2)
self.assertNotEqual(unique_id3, unique_id1)
# Make sure we can find the new target
found_target3 = self.dbg.FindTargetByGloballyUniqueID(
target3.GetGloballyUniqueID()
)
self.assertTrue(found_target3.IsValid())

def test_target_globally_unique_id_across_debuggers(self):
"""Test that target IDs are globally unique across multiple debuggers."""
self.build()
exe = self.getBuildArtifact("a.out")

# Create two debuggers with targets each
debugger1 = lldb.SBDebugger.Create()
debugger2 = lldb.SBDebugger.Create()
self.addTearDownHook(lambda: lldb.SBDebugger.Destroy(debugger1))
self.addTearDownHook(lambda: lldb.SBDebugger.Destroy(debugger2))

# Create 2 targets per debugger
targets_d1 = [debugger1.CreateTarget(exe), debugger1.CreateTarget(exe)]
targets_d2 = [debugger2.CreateTarget(exe), debugger2.CreateTarget(exe)]
targets = targets_d1 + targets_d2

# Get all IDs and verify they're unique
ids = [target.GetGloballyUniqueID() for target in targets]
self.assertEqual(
len(set(ids)), len(ids), f"IDs should be globally unique: {ids}"
)
self.assertTrue(
all(uid != lldb.LLDB_INVALID_GLOBALLY_UNIQUE_TARGET_ID for uid in ids),
"All IDs should be valid",
)

# Verify targets can be found by their IDs in respective debuggers
for debugger, target_pair in [
(debugger1, targets[:2]),
(debugger2, targets[2:]),
]:
for target in target_pair:
found = debugger.FindTargetByGloballyUniqueID(
target.GetGloballyUniqueID()
)
self.assertTrue(
found.IsValid(), "Target should be found by its unique ID"
)
self.assertEqual(
found.GetGloballyUniqueID(), target.GetGloballyUniqueID()
)
Loading