[Lldb-commits] [lldb] [lldb] Store the dummy target in the selected execution context (PR #190496)
via lldb-commits
lldb-commits at lists.llvm.org
Sat Apr 4 18:03:54 PDT 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-lldb
Author: Jonas Devlieghere (JDevlieghere)
<details>
<summary>Changes</summary>
Store the dummy target in the selected execution context. There's no reason for everybody to have to independently fall back to the dummy target.
---
Full diff: https://github.com/llvm/llvm-project/pull/190496.diff
7 Files Affected:
- (modified) lldb/include/lldb/Core/Debugger.h (+2-4)
- (modified) lldb/source/Commands/CommandObjectDWIMPrint.cpp (+2-4)
- (modified) lldb/source/Core/Debugger.cpp (+1-3)
- (modified) lldb/source/Interpreter/CommandInterpreter.cpp (+1-5)
- (modified) lldb/source/Interpreter/CommandObject.cpp (+14-16)
- (modified) lldb/source/Target/Target.cpp (+1-1)
- (modified) lldb/unittests/Core/DebuggerTest.cpp (+22)
``````````diff
diff --git a/lldb/include/lldb/Core/Debugger.h b/lldb/include/lldb/Core/Debugger.h
index fa4483c93e639..23fc34d29b8d7 100644
--- a/lldb/include/lldb/Core/Debugger.h
+++ b/lldb/include/lldb/Core/Debugger.h
@@ -208,12 +208,10 @@ class Debugger : public std::enable_shared_from_this<Debugger>,
}
/// Get the execution context representing the selected entities in the
- /// selected target.
+ /// selected target, or the dummy target if no target is selected.
ExecutionContext GetSelectedExecutionContext();
- /// Similar to GetSelectedExecutionContext but returns a
- /// ExecutionContextRef, and will hold the dummy target if no target is
- /// currently selected.
+ /// Like GetSelectedExecutionContext but returns an ExecutionContextRef.
ExecutionContextRef GetSelectedExecutionContextRef();
/// Get accessor for the target list.
diff --git a/lldb/source/Commands/CommandObjectDWIMPrint.cpp b/lldb/source/Commands/CommandObjectDWIMPrint.cpp
index 27bd71c21ad3f..ca9e9c72f1ba7 100644
--- a/lldb/source/Commands/CommandObjectDWIMPrint.cpp
+++ b/lldb/source/Commands/CommandObjectDWIMPrint.cpp
@@ -75,9 +75,7 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command,
auto verbosity = GetDebugger().GetDWIMPrintVerbosity();
- Target *target_ptr = m_exe_ctx.GetTargetPtr();
- // Fallback to the dummy target, which can allow for expression evaluation.
- Target &target = target_ptr ? *target_ptr : GetDummyTarget();
+ Target &target = m_exe_ctx.GetTargetRef();
EvaluateExpressionOptions eval_options =
m_expr_options.GetEvaluateExpressionOptions(target, m_varobj_options);
@@ -118,7 +116,7 @@ void CommandObjectDWIMPrint::DoExecute(StringRef command,
static const std::regex swift_class_regex(
"^<\\S+: 0x[[:xdigit:]]{5,}>\\s*$");
- if (GetDebugger().GetShowDontUsePoHint() && target_ptr &&
+ if (GetDebugger().GetShowDontUsePoHint() && !target.IsDummyTarget() &&
(language.AsLanguageType() == lldb::eLanguageTypeSwift ||
language.IsObjC()) &&
std::regex_match(output.data(), swift_class_regex)) {
diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp
index e1b2ce1b063e0..47a8603a5518e 100644
--- a/lldb/source/Core/Debugger.cpp
+++ b/lldb/source/Core/Debugger.cpp
@@ -1285,9 +1285,7 @@ void Debugger::RedrawStatusline(
}
ExecutionContext Debugger::GetSelectedExecutionContext() {
- bool adopt_selected = true;
- ExecutionContextRef exe_ctx_ref(GetSelectedTarget().get(), adopt_selected);
- return ExecutionContext(exe_ctx_ref);
+ return ExecutionContext(GetSelectedExecutionContextRef());
}
ExecutionContextRef Debugger::GetSelectedExecutionContextRef() {
diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp
index eeb1ae0ff3eb8..dba1f91c1a561 100644
--- a/lldb/source/Interpreter/CommandInterpreter.cpp
+++ b/lldb/source/Interpreter/CommandInterpreter.cpp
@@ -1972,11 +1972,7 @@ Status CommandInterpreter::PreprocessToken(std::string &expr_str) {
Status error;
ExecutionContext exe_ctx(GetExecutionContext());
- // Get a dummy target to allow for calculator mode while processing
- // backticks. This also helps break the infinite loop caused when target is
- // null.
- Target *exe_target = exe_ctx.GetTargetPtr();
- Target &target = exe_target ? *exe_target : m_debugger.GetDummyTarget();
+ Target &target = exe_ctx.GetTargetRef();
ValueObjectSP expr_result_valobj_sp;
diff --git a/lldb/source/Interpreter/CommandObject.cpp b/lldb/source/Interpreter/CommandObject.cpp
index 38a34496e73ff..3b3b2d7a302d9 100644
--- a/lldb/source/Interpreter/CommandObject.cpp
+++ b/lldb/source/Interpreter/CommandObject.cpp
@@ -147,7 +147,12 @@ bool CommandObject::CheckRequirements(CommandReturnObject &result) {
// we don't want any CommandObject instances to keep any of these objects
// around longer than for a single command. Every command should call
// CommandObject::Cleanup() after it has completed.
- assert(!m_exe_ctx.GetTargetPtr());
+ //
+ // The dummy target is allowed here because it is always alive, never causes
+ // resource leaks, and can appear when a command (e.g. "command source") is
+ // invoked re-entrantly before the outer Cleanup() has run.
+ assert(!m_exe_ctx.GetTargetPtr() ||
+ m_exe_ctx.GetTargetPtr()->IsDummyTarget());
assert(!m_exe_ctx.GetProcessPtr());
assert(!m_exe_ctx.GetThreadPtr());
assert(!m_exe_ctx.GetFramePtr());
@@ -162,13 +167,15 @@ bool CommandObject::CheckRequirements(CommandReturnObject &result) {
eCommandRequiresThread | eCommandRequiresFrame |
eCommandTryTargetAPILock)) {
- if ((flags & eCommandRequiresTarget) && !m_exe_ctx.HasTargetScope()) {
+ Target *target = m_exe_ctx.GetTargetPtr();
+ if ((flags & eCommandRequiresTarget) &&
+ (!target || target->IsDummyTarget())) {
result.AppendError(GetInvalidTargetDescription());
return false;
}
if ((flags & eCommandRequiresProcess) && !m_exe_ctx.HasProcessScope()) {
- if (!m_exe_ctx.HasTargetScope())
+ if (!target || target->IsDummyTarget())
result.AppendError(GetInvalidTargetDescription());
else
result.AppendError(GetInvalidProcessDescription());
@@ -176,7 +183,7 @@ bool CommandObject::CheckRequirements(CommandReturnObject &result) {
}
if ((flags & eCommandRequiresThread) && !m_exe_ctx.HasThreadScope()) {
- if (!m_exe_ctx.HasTargetScope())
+ if (!target || target->IsDummyTarget())
result.AppendError(GetInvalidTargetDescription());
else if (!m_exe_ctx.HasProcessScope())
result.AppendError(GetInvalidProcessDescription());
@@ -186,7 +193,7 @@ bool CommandObject::CheckRequirements(CommandReturnObject &result) {
}
if ((flags & eCommandRequiresFrame) && !m_exe_ctx.HasFrameScope()) {
- if (!m_exe_ctx.HasTargetScope())
+ if (!target || target->IsDummyTarget())
result.AppendError(GetInvalidTargetDescription());
else if (!m_exe_ctx.HasProcessScope())
result.AppendError(GetInvalidProcessDescription());
@@ -204,8 +211,7 @@ bool CommandObject::CheckRequirements(CommandReturnObject &result) {
}
if (flags & eCommandTryTargetAPILock) {
- Target *target = m_exe_ctx.GetTargetPtr();
- if (target)
+ if (target && !target->IsDummyTarget())
m_api_locker =
std::unique_lock<std::recursive_mutex>(target->GetAPIMutex());
}
@@ -761,15 +767,7 @@ Target &CommandObject::GetTarget() {
// Fallback to the command interpreter's execution context in case we get
// called after DoExecute has finished. For example, when doing multi-line
// expression that uses an input reader or breakpoint callbacks.
- if (Target *target = m_interpreter.GetExecutionContext().GetTargetPtr())
- return *target;
-
- // Finally, if we have no other target, get the selected target.
- if (TargetSP target_sp = m_interpreter.GetDebugger().GetSelectedTarget())
- return *target_sp;
-
- // We only have the dummy target.
- return GetDummyTarget();
+ return m_interpreter.GetExecutionContext().GetTargetRef();
}
Thread *CommandObject::GetDefaultThread() {
diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp
index 0168c7d686e37..538cd6b3a6e7c 100644
--- a/lldb/source/Target/Target.cpp
+++ b/lldb/source/Target/Target.cpp
@@ -4440,7 +4440,7 @@ class TargetOptionValueProperties
// we just use the one from this instance.
if (exe_ctx) {
Target *target = exe_ctx->GetTargetPtr();
- if (target) {
+ if (target && !target->IsDummyTarget()) {
TargetOptionValueProperties *target_properties =
static_cast<TargetOptionValueProperties *>(
target->GetValueProperties().get());
diff --git a/lldb/unittests/Core/DebuggerTest.cpp b/lldb/unittests/Core/DebuggerTest.cpp
index 4dccd912c63ae..0897f682c7aa6 100644
--- a/lldb/unittests/Core/DebuggerTest.cpp
+++ b/lldb/unittests/Core/DebuggerTest.cpp
@@ -12,6 +12,7 @@
#include "TestingSupport/TestUtilities.h"
#include "lldb/Host/FileSystem.h"
#include "lldb/Host/HostInfo.h"
+#include "lldb/Target/ExecutionContext.h"
#include "gtest/gtest.h"
using namespace lldb;
@@ -50,3 +51,24 @@ TEST_F(DebuggerTest, TestSettings) {
Debugger::Destroy(debugger_sp);
}
+
+TEST_F(DebuggerTest,
+ SelectedExecutionContextUsesDummyTargetWhenNoTargetSelected) {
+ DebuggerSP debugger_sp = Debugger::CreateInstance();
+
+ // No targets have been added, so no target is selected.
+ ASSERT_EQ(debugger_sp->GetSelectedTarget().get(), nullptr);
+
+ Target &dummy_target = debugger_sp->GetDummyTarget();
+
+ // GetSelectedExecutionContextRef should fall back to the dummy target.
+ ExecutionContextRef exe_ctx_ref =
+ debugger_sp->GetSelectedExecutionContextRef();
+ EXPECT_EQ(exe_ctx_ref.GetTargetSP().get(), &dummy_target);
+
+ // GetSelectedExecutionContext should also contain the dummy target.
+ ExecutionContext exe_ctx = debugger_sp->GetSelectedExecutionContext();
+ EXPECT_EQ(exe_ctx.GetTargetPtr(), &dummy_target);
+
+ Debugger::Destroy(debugger_sp);
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/190496
More information about the lldb-commits
mailing list