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
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.
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.
///
/// \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