[Lldb-commits] [lldb] [lldb] Speed up evaluating breakpoint conditions by using DIL (PR #224740)
Ilia Kuklin via lldb-commits
lldb-commits at lists.llvm.org
Thu Sep 24 12:03:01 PDT 2026
https://github.com/kuilpd updated https://github.com/llvm/llvm-project/pull/224740
>From 6463897a87a4c14569085bcff9a7530090e222cc Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Wed, 16 Sep 2026 23:31:19 +0500
Subject: [PATCH 1/7] [lldb] Speed up evaluating breakpoint conditions by using
DIL
---
.../lldb/Breakpoint/BreakpointLocation.h | 3 +
lldb/include/lldb/Target/Target.h | 4 +
lldb/source/Breakpoint/BreakpointLocation.cpp | 67 +++++++++++++++-
lldb/source/Target/Target.cpp | 22 ++++++
lldb/source/Target/TargetProperties.td | 3 +
.../TestBreakpointConditions.py | 76 +++++++++++++++++++
6 files changed, 174 insertions(+), 1 deletion(-)
diff --git a/lldb/include/lldb/Breakpoint/BreakpointLocation.h b/lldb/include/lldb/Breakpoint/BreakpointLocation.h
index d7eef1966bb31..f3e3204466388 100644
--- a/lldb/include/lldb/Breakpoint/BreakpointLocation.h
+++ b/lldb/include/lldb/Breakpoint/BreakpointLocation.h
@@ -18,6 +18,7 @@
#include "lldb/Core/Address.h"
#include "lldb/Symbol/LineEntry.h"
#include "lldb/Utility/UserID.h"
+#include "lldb/ValueObject/DILAST.h"
#include "lldb/lldb-private.h"
namespace lldb_private {
@@ -420,6 +421,8 @@ class BreakpointLocation
lldb::BreakpointSiteSP m_bp_site_sp;
///< The compiled expression to use in testing our condition.
lldb::UserExpressionSP m_user_expression_sp;
+ ///< The expression parsed by Data Inspection Language (DIL).
+ dil::ASTNodeUP m_dil_expr_tree;
///< Guards parsing and evaluation of the condition, which could be evaluated
/// by multiple processes.
std::mutex m_condition_mutex;
diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h
index 0b4290ad4a33d..4f91d979eec9e 100644
--- a/lldb/include/lldb/Target/Target.h
+++ b/lldb/include/lldb/Target/Target.h
@@ -285,6 +285,10 @@ class TargetProperties : public Properties {
void SetUseDILForCreatingValues(bool b);
+ bool GetUseDILForBreakpointConditions() const;
+
+ void SetUseDILForBreakpointConditions(bool b);
+
void SetRequireHardwareBreakpoints(bool b);
bool GetRequireHardwareBreakpoints() const;
diff --git a/lldb/source/Breakpoint/BreakpointLocation.cpp b/lldb/source/Breakpoint/BreakpointLocation.cpp
index fc679ffd662ea..ad8420ab92aa2 100644
--- a/lldb/source/Breakpoint/BreakpointLocation.cpp
+++ b/lldb/source/Breakpoint/BreakpointLocation.cpp
@@ -26,6 +26,9 @@
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Log.h"
#include "lldb/Utility/StreamString.h"
+#include "lldb/ValueObject/DILEval.h"
+#include "lldb/ValueObject/DILLexer.h"
+#include "lldb/ValueObject/DILParser.h"
#include "lldb/ValueObject/ValueObject.h"
using namespace lldb;
@@ -226,10 +229,68 @@ bool BreakpointLocation::ConditionSaysStop(ExecutionContext &exe_ctx,
StopCondition condition = GetCondition();
if (!condition) {
+ m_dil_expr_tree.reset();
m_user_expression_sp.reset();
return false;
}
+ // Attempt to parse the condition using Data Inspection Language (DIL).
+ if (condition.GetHash() != m_condition_hash && exe_ctx.HasFrameScope() &&
+ exe_ctx.GetTargetSP()->GetUseDILForBreakpointConditions()) {
+ // Lex the expression.
+ auto lex_or_err = dil::DILLexer::Create(condition.GetText(), eDILModeFull);
+ if (lex_or_err) {
+ // Parse the expression.
+ auto tree_or_error = dil::DILParser::Parse(
+ condition.GetText(), std::move(*lex_or_err), exe_ctx.GetFrameRef(),
+ lldb::eNoDynamicValues, eDILModeFull);
+ if (tree_or_error) {
+ m_dil_expr_tree = std::move(*tree_or_error);
+ m_condition_hash = condition.GetHash();
+ LLDB_LOG(log, "DIL successfully parsed the condition: {0}.",
+ condition.GetText());
+ } else {
+ m_dil_expr_tree.reset();
+ error = Status::FromError(tree_or_error.takeError());
+ LLDB_LOG(log, "Parsing condition with DIL failed:\n{0}.", error);
+ }
+ } else {
+ m_dil_expr_tree.reset();
+ error = Status::FromError(lex_or_err.takeError());
+ LLDB_LOG(log, "Lexing condition with DIL failed:\n{0}.", error);
+ }
+ }
+ // If the expression was parsed successfully, it can be evaluated separately
+ // at every breakpoint without having to parse it again.
+ if (m_dil_expr_tree) {
+ dil::Interpreter interpreter(exe_ctx.GetTargetSP(), condition.GetText(),
+ exe_ctx.GetFrameRef(), lldb::eNoDynamicValues,
+ 0);
+ // Evaluate the expression by DIL.
+ auto valobj_or_error = interpreter.EvaluateTree(m_dil_expr_tree);
+ if (valobj_or_error) {
+ ValueObjectSP value_sp = *valobj_or_error;
+ if (value_sp && value_sp->GetError().Success()) {
+ llvm::Expected<bool> bool_result = value_sp->GetValueAsBool();
+ if (bool_result) {
+ LLDB_LOG(log,
+ "Condition successfully evaluated by DIL, result is {0}.\n",
+ *bool_result ? "true" : "false");
+ return *bool_result;
+ }
+ error = Status::FromError(bool_result.takeError());
+ }
+ } else {
+ error = Status::FromError(valobj_or_error.takeError());
+ }
+ // Evaluation failed.
+ LLDB_LOG(log, "Evaluating condition with DIL failed:\n{0}.", error);
+ m_dil_expr_tree.reset();
+ m_condition_hash = 0;
+ }
+ // If DIL failed at any point, the condition evaluation falls back to
+ // UserExpression.
+
error.Clear();
DiagnosticManager diagnostics;
@@ -306,7 +367,9 @@ bool BreakpointLocation::ConditionSaysStop(ExecutionContext &exe_ctx,
ret = result_value_sp->IsLogicalTrue(error);
if (log) {
if (error.Success()) {
- LLDB_LOGF(log, "Condition successfully evaluated, result is %s.\n",
+ LLDB_LOGF(log,
+ "Condition successfully evaluated by UserExpression, "
+ "result is %s.\n",
ret ? "true" : "false");
} else {
error = Status::FromErrorString(
@@ -804,7 +867,9 @@ void BreakpointLocation::SwapLocation(BreakpointLocationSP swap_from) {
swap_from->m_should_resolve_indirect_functions;
m_is_reexported = swap_from->m_is_reexported;
m_is_indirect = swap_from->m_is_indirect;
+ m_dil_expr_tree.reset();
m_user_expression_sp.reset();
+ m_condition_hash = 0;
}
void BreakpointLocation::SetThreadIDInternal(lldb::tid_t thread_id) {
diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp
index bff77dbc8b993..549089d5d792c 100644
--- a/lldb/source/Target/Target.cpp
+++ b/lldb/source/Target/Target.cpp
@@ -5283,6 +5283,28 @@ void TargetProperties::SetUseDILForCreatingValues(bool b) {
exp_values->SetPropertyAtIndex(ePropertyUseDILForCreatingValues, b);
}
+bool TargetProperties::GetUseDILForBreakpointConditions() const {
+ const Property *exp_property =
+ m_collection_sp->GetPropertyAtIndex(ePropertyExperimental);
+ OptionValueProperties *exp_values =
+ exp_property->GetValue()->GetAsProperties();
+ if (exp_values)
+ return exp_values
+ ->GetPropertyAtIndexAs<bool>(ePropertyUseDILForBreakpointConditions)
+ .value_or(false);
+ else
+ return true;
+}
+
+void TargetProperties::SetUseDILForBreakpointConditions(bool b) {
+ const Property *exp_property =
+ m_collection_sp->GetPropertyAtIndex(ePropertyExperimental);
+ OptionValueProperties *exp_values =
+ exp_property->GetValue()->GetAsProperties();
+ if (exp_values)
+ exp_values->SetPropertyAtIndex(ePropertyUseDILForBreakpointConditions, b);
+}
+
ArchSpec TargetProperties::GetDefaultArchitecture() const {
const uint32_t idx = ePropertyDefaultArch;
return GetPropertyAtIndexAs<ArchSpec>(idx, {});
diff --git a/lldb/source/Target/TargetProperties.td b/lldb/source/Target/TargetProperties.td
index 5474607bbf838..af1b7be590f71 100644
--- a/lldb/source/Target/TargetProperties.td
+++ b/lldb/source/Target/TargetProperties.td
@@ -10,6 +10,9 @@ let Definition = "target_experimental", Path = "target.experimental" in {
def UseDILForCreatingValues: Property<"use-DIL-for-creating-values", "Boolean">,
Global, DefaultTrue,
Desc<"If true, use the DIL implementation in API SBValue::CreateValueFromExpression.">;
+ def UseDILForBreakpointConditions: Property<"use-DIL-for-breakpoint-conditions", "Boolean">,
+ Global, DefaultTrue,
+ Desc<"If true, use the DIL implementation to evaluate conditions for breakpoints.">;
}
let Definition = "target", Path = "target" in {
diff --git a/lldb/test/API/functionalities/breakpoint/breakpoint_conditions/TestBreakpointConditions.py b/lldb/test/API/functionalities/breakpoint/breakpoint_conditions/TestBreakpointConditions.py
index d9f00af406b3d..35e60e3c7e58d 100644
--- a/lldb/test/API/functionalities/breakpoint/breakpoint_conditions/TestBreakpointConditions.py
+++ b/lldb/test/API/functionalities/breakpoint/breakpoint_conditions/TestBreakpointConditions.py
@@ -42,6 +42,11 @@ def test_breakpoint_invalid_condition_and_python_api(self):
self.build()
self.breakpoint_invalid_conditions_python()
+ def test_breakpoint_condition_with_DIL(self):
+ """Test evaluating breakpoint conditions with Data Inspection Language."""
+ self.build()
+ self.breakpoint_conditions_with_DIL()
+
def setUp(self):
# Call super's setUp().
TestBase.setUp(self)
@@ -260,3 +265,74 @@ def breakpoint_invalid_conditions_python(self):
# The hit count for the breakpoint should be 1.
self.assertEqual(breakpoint.GetHitCount(), 1)
+
+ def breakpoint_conditions_with_DIL(self):
+ """Test evaluating breakpoint conditions with Data Inspection Language."""
+ exe = self.getBuildArtifact("a.out")
+ self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
+ self.runCmd(
+ "settings set target.experimental.use-DIL-for-breakpoint-conditions true"
+ )
+
+ def break_on_symbol_with_condition(symbol, condition):
+ lldbutil.run_break_set_by_symbol(
+ self,
+ symbol,
+ extra_options=" -c '{}' -Y c++".format(condition),
+ num_expected_locations=1,
+ sym_exact=True,
+ )
+
+ # Create a breakpoint with a condition that can be evaluated by DIL.
+ break_on_symbol_with_condition("a", "val == 1")
+ # Create a breakpoint with a condition where DIL fails during lexing.
+ break_on_symbol_with_condition("b", "val # 1")
+ # Create a breakpoint with a condition where DIL fails during parsing.
+ break_on_symbol_with_condition("b", "val == ?")
+ # Create a breakpoint with a condition where DIL fails during evaluating.
+ break_on_symbol_with_condition("b", "val == no_such_variable")
+ # Create a breakpoint with a condition where DIL fails during evaluating
+ # and successfully falls back to UserExpression.
+ break_on_symbol_with_condition("c", "val == static_cast<int>(1.0)")
+
+ # Enable logging
+ log_file = self.getBuildArtifact("log-file.txt")
+ if os.path.exists(log_file):
+ os.remove(log_file)
+ self.runCmd("log enable -f '%s' lldb break" % (log_file))
+
+ # Run the program until breakpoint "a"
+ self.runCmd("run", RUN_SUCCEEDED)
+ # Continue until breakpoint "b"
+ self.runCmd("continue")
+ # Continue until breakpoint "c"
+ self.runCmd("continue")
+
+ self.expect("process status", PROCESS_STOPPED, patterns=["Process .* stopped"])
+
+ self.filecheck_log(log_file, __file__)
+
+ ## Check the breakpoint "a" was evaluated successfully by DIL.
+ # CHECK: DIL successfully parsed the condition: val == 1
+ # CHECK: Condition successfully evaluated by DIL, result is true.
+
+ ## Check that when DIL's lexer, parser and interpreter fail at breakpoint "b",
+ ## the evaluation falls back to UserExpression, which should also fail.
+ ## condition: "val # 1"
+ # CHECK: Lexing condition with DIL failed
+ # CHECK: Error evaluating condition
+ ## condition: "val == ?"
+ # CHECK: Parsing condition with DIL failed
+ # CHECK: Error evaluating condition
+ ## condition: "val == no_such_variable"
+ # CHECK: DIL successfully parsed the condition
+ # CHECK: Evaluating condition with DIL failed
+ # CHECK: Error evaluating condition
+
+ ## Check that when DIL fails evaluating at breakpoint "c",
+ ## the evaluation falls back to UserExpression and succeeds.
+ # CHECK: DIL successfully parsed the condition: val == static_cast<int>(1.0)
+ # CHECK: Evaluating condition with DIL failed
+ # CHECK: Condition successfully evaluated by UserExpression, result is true.
+
+ self.runCmd("process kill")
>From 0679328ac9b9f8d0a34bb5e8c9a38c45ade71c8a Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Tue, 22 Sep 2026 20:51:35 +0500
Subject: [PATCH 2/7] Remove UseDILForBreakpointConditions setting
---
lldb/include/lldb/Target/Target.h | 4 ----
lldb/source/Breakpoint/BreakpointLocation.cpp | 3 +--
lldb/source/Target/Target.cpp | 22 -------------------
lldb/source/Target/TargetProperties.td | 3 ---
4 files changed, 1 insertion(+), 31 deletions(-)
diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h
index 4f91d979eec9e..0b4290ad4a33d 100644
--- a/lldb/include/lldb/Target/Target.h
+++ b/lldb/include/lldb/Target/Target.h
@@ -285,10 +285,6 @@ class TargetProperties : public Properties {
void SetUseDILForCreatingValues(bool b);
- bool GetUseDILForBreakpointConditions() const;
-
- void SetUseDILForBreakpointConditions(bool b);
-
void SetRequireHardwareBreakpoints(bool b);
bool GetRequireHardwareBreakpoints() const;
diff --git a/lldb/source/Breakpoint/BreakpointLocation.cpp b/lldb/source/Breakpoint/BreakpointLocation.cpp
index ad8420ab92aa2..e23cf70d0471e 100644
--- a/lldb/source/Breakpoint/BreakpointLocation.cpp
+++ b/lldb/source/Breakpoint/BreakpointLocation.cpp
@@ -235,8 +235,7 @@ bool BreakpointLocation::ConditionSaysStop(ExecutionContext &exe_ctx,
}
// Attempt to parse the condition using Data Inspection Language (DIL).
- if (condition.GetHash() != m_condition_hash && exe_ctx.HasFrameScope() &&
- exe_ctx.GetTargetSP()->GetUseDILForBreakpointConditions()) {
+ if (condition.GetHash() != m_condition_hash && exe_ctx.HasFrameScope()) {
// Lex the expression.
auto lex_or_err = dil::DILLexer::Create(condition.GetText(), eDILModeFull);
if (lex_or_err) {
diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp
index 549089d5d792c..bff77dbc8b993 100644
--- a/lldb/source/Target/Target.cpp
+++ b/lldb/source/Target/Target.cpp
@@ -5283,28 +5283,6 @@ void TargetProperties::SetUseDILForCreatingValues(bool b) {
exp_values->SetPropertyAtIndex(ePropertyUseDILForCreatingValues, b);
}
-bool TargetProperties::GetUseDILForBreakpointConditions() const {
- const Property *exp_property =
- m_collection_sp->GetPropertyAtIndex(ePropertyExperimental);
- OptionValueProperties *exp_values =
- exp_property->GetValue()->GetAsProperties();
- if (exp_values)
- return exp_values
- ->GetPropertyAtIndexAs<bool>(ePropertyUseDILForBreakpointConditions)
- .value_or(false);
- else
- return true;
-}
-
-void TargetProperties::SetUseDILForBreakpointConditions(bool b) {
- const Property *exp_property =
- m_collection_sp->GetPropertyAtIndex(ePropertyExperimental);
- OptionValueProperties *exp_values =
- exp_property->GetValue()->GetAsProperties();
- if (exp_values)
- exp_values->SetPropertyAtIndex(ePropertyUseDILForBreakpointConditions, b);
-}
-
ArchSpec TargetProperties::GetDefaultArchitecture() const {
const uint32_t idx = ePropertyDefaultArch;
return GetPropertyAtIndexAs<ArchSpec>(idx, {});
diff --git a/lldb/source/Target/TargetProperties.td b/lldb/source/Target/TargetProperties.td
index af1b7be590f71..5474607bbf838 100644
--- a/lldb/source/Target/TargetProperties.td
+++ b/lldb/source/Target/TargetProperties.td
@@ -10,9 +10,6 @@ let Definition = "target_experimental", Path = "target.experimental" in {
def UseDILForCreatingValues: Property<"use-DIL-for-creating-values", "Boolean">,
Global, DefaultTrue,
Desc<"If true, use the DIL implementation in API SBValue::CreateValueFromExpression.">;
- def UseDILForBreakpointConditions: Property<"use-DIL-for-breakpoint-conditions", "Boolean">,
- Global, DefaultTrue,
- Desc<"If true, use the DIL implementation to evaluate conditions for breakpoints.">;
}
let Definition = "target", Path = "target" in {
>From 168c84b0e9f538e18b990b3f64478933f6d10edc Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Tue, 22 Sep 2026 23:37:35 +0500
Subject: [PATCH 3/7] Add -V <condition-mode> option
---
lldb/include/lldb/Breakpoint/StopCondition.h | 8 ++++++++
.../lldb/Interpreter/CommandOptionArgumentTable.h | 10 ++++++++++
lldb/include/lldb/lldb-enumerations.h | 8 ++++++++
lldb/source/Breakpoint/BreakpointLocation.cpp | 9 ++++++---
lldb/source/Commands/CommandObjectBreakpoint.cpp | 7 +++++++
lldb/source/Commands/Options.td | 12 ++++++++++--
6 files changed, 49 insertions(+), 5 deletions(-)
diff --git a/lldb/include/lldb/Breakpoint/StopCondition.h b/lldb/include/lldb/Breakpoint/StopCondition.h
index 9106d57435af5..ff21767f4e927 100644
--- a/lldb/include/lldb/Breakpoint/StopCondition.h
+++ b/lldb/include/lldb/Breakpoint/StopCondition.h
@@ -39,6 +39,10 @@ class StopCondition {
void SetLanguage(lldb::LanguageType language) { m_language = language; }
+ lldb::BreakpointConditionMode GetMode() const { return m_condition_mode; }
+
+ void SetMode(lldb::BreakpointConditionMode mode) { m_condition_mode = mode; }
+
private:
/// The condition to test.
std::string m_text;
@@ -48,6 +52,10 @@ class StopCondition {
/// The language for this condition.
lldb::LanguageType m_language = lldb::eLanguageTypeUnknown;
+
+ /// The evaluation mode for this condition.
+ lldb::BreakpointConditionMode m_condition_mode =
+ lldb::eBreakpointConditionModeDWIM;
};
} // namespace lldb_private
diff --git a/lldb/include/lldb/Interpreter/CommandOptionArgumentTable.h b/lldb/include/lldb/Interpreter/CommandOptionArgumentTable.h
index 7075a7acb9733..ad4210066626f 100644
--- a/lldb/include/lldb/Interpreter/CommandOptionArgumentTable.h
+++ b/lldb/include/lldb/Interpreter/CommandOptionArgumentTable.h
@@ -243,6 +243,15 @@ static constexpr OptionEnumValueElement g_completion_type[] = {
{lldb::eThreadIDCompletion, "thread-id", "Completes to a thread ID."},
};
+static constexpr OptionEnumValueElement g_breakpoint_condition_mode[] = {
+ {lldb::eBreakpointConditionModeDIL, "dil",
+ "Use Data Inspection Language to evaluate the condition."},
+ {lldb::eBreakpointConditionModeExpr, "expr",
+ "Use UserExpression to evaluate the condition."},
+ {lldb::eBreakpointConditionModeDWIM, "dwim",
+ "Use DIL to evaluate the condition, and if it fails, "
+ "fall back to UserExpression."}};
+
llvm::StringRef RegisterNameHelpTextCallback();
llvm::StringRef BreakpointIDHelpTextCallback();
llvm::StringRef BreakpointIDRangeHelpTextCallback();
@@ -262,6 +271,7 @@ static constexpr CommandObject::ArgumentTableEntry g_argument_table[] = {
{ lldb::eArgTypeAliasOptions, "options-for-aliased-command", lldb::CompletionType::eNoCompletion, {}, { nullptr, false }, "Command options to be used as part of an alias (abbreviation) definition. (See 'help commands alias' for more information.)" },
{ lldb::eArgTypeArchitecture, "arch", lldb::eArchitectureCompletion, {}, { arch_helper, true }, "The architecture name, e.g. i386 or x86_64." },
{ lldb::eArgTypeBoolean, "boolean", lldb::CompletionType::eNoCompletion, {}, { nullptr, false }, "A Boolean value: 'true' or 'false'" },
+ { lldb::eArgTypeBreakpointConditionMode, "condition-mode", lldb::CompletionType::eNoCompletion, g_breakpoint_condition_mode, { nullptr, false }, "Specifies the mode to use when evaluating a breakpoint's condition expression." },
{ lldb::eArgTypeBreakpointID, "breakpt-id", lldb::CompletionType::eNoCompletion, {}, { BreakpointIDHelpTextCallback, false }, nullptr },
{ lldb::eArgTypeBreakpointIDRange, "breakpt-id-list", lldb::CompletionType::eNoCompletion, {}, { BreakpointIDRangeHelpTextCallback, false }, nullptr },
{ lldb::eArgTypeBreakpointName, "breakpoint-name", lldb::eBreakpointNameCompletion, {}, { BreakpointNameHelpTextCallback, false }, nullptr },
diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h
index b79e4ff9a56fb..af060cd596aea 100644
--- a/lldb/include/lldb/lldb-enumerations.h
+++ b/lldb/include/lldb/lldb-enumerations.h
@@ -712,6 +712,7 @@ enum CommandArgumentType {
eArgTypeAliasOptions,
eArgTypeArchitecture,
eArgTypeBoolean,
+ eArgTypeBreakpointConditionMode,
eArgTypeBreakpointID,
eArgTypeBreakpointIDRange,
eArgTypeBreakpointName,
@@ -1631,6 +1632,13 @@ constexpr unsigned BreakpointResolverAllResolversMask =
eResolverFileAndLine | eResolverAddress | eResolverName |
eResolverFileRegex | eResolverPython | eResolverException;
+/// Modes for evaluating breakpoint conditions.
+enum BreakpointConditionMode {
+ eBreakpointConditionModeDIL,
+ eBreakpointConditionModeExpr,
+ eBreakpointConditionModeDWIM
+};
+
} // namespace lldb
#endif // LLDB_LLDB_ENUMERATIONS_H
diff --git a/lldb/source/Breakpoint/BreakpointLocation.cpp b/lldb/source/Breakpoint/BreakpointLocation.cpp
index e23cf70d0471e..a8cdf777c3eb6 100644
--- a/lldb/source/Breakpoint/BreakpointLocation.cpp
+++ b/lldb/source/Breakpoint/BreakpointLocation.cpp
@@ -235,7 +235,8 @@ bool BreakpointLocation::ConditionSaysStop(ExecutionContext &exe_ctx,
}
// Attempt to parse the condition using Data Inspection Language (DIL).
- if (condition.GetHash() != m_condition_hash && exe_ctx.HasFrameScope()) {
+ if (condition.GetHash() != m_condition_hash && exe_ctx.HasFrameScope() &&
+ condition.GetMode() != lldb::eBreakpointConditionModeExpr) {
// Lex the expression.
auto lex_or_err = dil::DILLexer::Create(condition.GetText(), eDILModeFull);
if (lex_or_err) {
@@ -287,9 +288,11 @@ bool BreakpointLocation::ConditionSaysStop(ExecutionContext &exe_ctx,
m_dil_expr_tree.reset();
m_condition_hash = 0;
}
- // If DIL failed at any point, the condition evaluation falls back to
- // UserExpression.
+ // DIL evaluation failed, trigger the breakpoint and return the error.
+ if (condition.GetMode() == lldb::eBreakpointConditionModeDIL)
+ return true;
+ // The condition evaluation proceeds to UserExpression here.
error.Clear();
DiagnosticManager diagnostics;
diff --git a/lldb/source/Commands/CommandObjectBreakpoint.cpp b/lldb/source/Commands/CommandObjectBreakpoint.cpp
index 216ba5754182b..7d642d514cf8c 100644
--- a/lldb/source/Commands/CommandObjectBreakpoint.cpp
+++ b/lldb/source/Commands/CommandObjectBreakpoint.cpp
@@ -201,6 +201,13 @@ class lldb_private::BreakpointOptionGroup : public OptionGroup {
else
m_bp_opts.GetCondition().SetLanguage(language);
} break;
+ case 'Z': {
+ auto condition_mode =
+ static_cast<BreakpointConditionMode>(OptionArgParser::ToOptionEnum(
+ option_arg, GetDefinitions()[option_idx].enum_values,
+ lldb::eBreakpointConditionModeDWIM, error));
+ m_bp_opts.GetCondition().SetMode(condition_mode);
+ } break;
default:
llvm_unreachable("Unimplemented option");
}
diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td
index ab851725979ef..16e5b6292b3a7 100644
--- a/lldb/source/Commands/Options.td
+++ b/lldb/source/Commands/Options.td
@@ -141,13 +141,21 @@ let Command = "breakpoint modify" in {
Group<1>,
Arg<"Expression">,
Desc<"The breakpoint stops only if this ${c}ondition expression "
- "evaluates to true.">;
+ "evaluates to true. By default, DIL is used to evaluate the "
+ "expression, and if it fails, the evaluation falls back to "
+ "UserExpression. Use -Z <condition-mode> to change this behavior.">;
+ def breakpoint_modify_condition_mode
+ : Option<"condition-mode", "Z">,
+ Group<1>,
+ EnumArg<"BreakpointConditionMode">,
+ Desc<"Specifies the mode to use when evaluating the breakpoint's "
+ "condition expression. Default: dwim.">;
def breakpoint_modify_condition_language
: Option<"condition-language", "Y">,
Group<1>,
Arg<"Language">,
Desc<"Specifies the Language to use when executing the breakpoint's "
- "condition expression.">;
+ "condition expression using UserExpression.">;
def breakpoint_modify_auto_continue
: Option<"auto-continue", "G">,
Group<1>,
>From 32dadc27df0e0ba298826e5c61661d463cee5266 Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Wed, 23 Sep 2026 00:26:39 +0500
Subject: [PATCH 4/7] Update the test
---
.../TestBreakpointConditions.py | 40 +++++++++++--------
1 file changed, 23 insertions(+), 17 deletions(-)
diff --git a/lldb/test/API/functionalities/breakpoint/breakpoint_conditions/TestBreakpointConditions.py b/lldb/test/API/functionalities/breakpoint/breakpoint_conditions/TestBreakpointConditions.py
index 35e60e3c7e58d..4672e2d08fd4c 100644
--- a/lldb/test/API/functionalities/breakpoint/breakpoint_conditions/TestBreakpointConditions.py
+++ b/lldb/test/API/functionalities/breakpoint/breakpoint_conditions/TestBreakpointConditions.py
@@ -270,30 +270,28 @@ def breakpoint_conditions_with_DIL(self):
"""Test evaluating breakpoint conditions with Data Inspection Language."""
exe = self.getBuildArtifact("a.out")
self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
- self.runCmd(
- "settings set target.experimental.use-DIL-for-breakpoint-conditions true"
- )
- def break_on_symbol_with_condition(symbol, condition):
+ def break_on_symbol_with_condition(symbol, condition, mode):
lldbutil.run_break_set_by_symbol(
self,
symbol,
- extra_options=" -c '{}' -Y c++".format(condition),
+ extra_options=" -c '{}' -Z {} -Y C++".format(condition, mode),
num_expected_locations=1,
sym_exact=True,
)
- # Create a breakpoint with a condition that can be evaluated by DIL.
- break_on_symbol_with_condition("a", "val == 1")
+ # Create a breakpoint with a valid condition.
+ break_on_symbol_with_condition("a", "val == 1", "dwim")
# Create a breakpoint with a condition where DIL fails during lexing.
- break_on_symbol_with_condition("b", "val # 1")
+ break_on_symbol_with_condition("b", "val # 1", "dwim")
# Create a breakpoint with a condition where DIL fails during parsing.
- break_on_symbol_with_condition("b", "val == ?")
+ break_on_symbol_with_condition("b", "val == ?", "dwim")
# Create a breakpoint with a condition where DIL fails during evaluating.
- break_on_symbol_with_condition("b", "val == no_such_variable")
- # Create a breakpoint with a condition where DIL fails during evaluating
- # and successfully falls back to UserExpression.
- break_on_symbol_with_condition("c", "val == static_cast<int>(1.0)")
+ break_on_symbol_with_condition("b", "val == no_such_variable", "dwim")
+ # Create a breakpoint with a valid condition where DIL fails during evaluating.
+ break_on_symbol_with_condition("c", "val == static_cast<int>(1.0)", "dil")
+ break_on_symbol_with_condition("c", "val == static_cast<int>(1.0)", "expr")
+ break_on_symbol_with_condition("c", "val == static_cast<int>(1.0)", "dwim")
# Enable logging
log_file = self.getBuildArtifact("log-file.txt")
@@ -320,16 +318,24 @@ def break_on_symbol_with_condition(symbol, condition):
## the evaluation falls back to UserExpression, which should also fail.
## condition: "val # 1"
# CHECK: Lexing condition with DIL failed
- # CHECK: Error evaluating condition
+ # CHECK: error: <user expression
## condition: "val == ?"
# CHECK: Parsing condition with DIL failed
- # CHECK: Error evaluating condition
+ # CHECK: error: <user expression
## condition: "val == no_such_variable"
# CHECK: DIL successfully parsed the condition
# CHECK: Evaluating condition with DIL failed
- # CHECK: Error evaluating condition
+ # CHECK: error: <user expression
- ## Check that when DIL fails evaluating at breakpoint "c",
+ ## Check that when DIL fails evaluating at breakpoint "c" with "dil" mode,
+ ## the evaluation does not fall back to UserExpression.
+ # CHECK: Evaluating condition with DIL failed
+ # CHECK: Error evaluating condition
+ ## Check that the breakpoint "c" with "expr" mode was not attempted by DIL
+ ## and was successfully evaluated by UserExpression.
+ # CHECK-NOT: DIL
+ # CHECK: Condition successfully evaluated by UserExpression, result is true
+ ## Check that when DIL fails evaluating at breakpoint "c" with "dwim" mode,
## the evaluation falls back to UserExpression and succeeds.
# CHECK: DIL successfully parsed the condition: val == static_cast<int>(1.0)
# CHECK: Evaluating condition with DIL failed
>From 4bd772742edb06613201415e449cfcc005d9df12 Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Wed, 23 Sep 2026 22:52:10 +0500
Subject: [PATCH 5/7] Update comment
---
lldb/source/Breakpoint/BreakpointLocation.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/lldb/source/Breakpoint/BreakpointLocation.cpp b/lldb/source/Breakpoint/BreakpointLocation.cpp
index a8cdf777c3eb6..4061f2861b6f3 100644
--- a/lldb/source/Breakpoint/BreakpointLocation.cpp
+++ b/lldb/source/Breakpoint/BreakpointLocation.cpp
@@ -261,7 +261,7 @@ bool BreakpointLocation::ConditionSaysStop(ExecutionContext &exe_ctx,
}
}
// If the expression was parsed successfully, it can be evaluated separately
- // at every breakpoint without having to parse it again.
+ // at every breakpoint location hit without having to parse it again.
if (m_dil_expr_tree) {
dil::Interpreter interpreter(exe_ctx.GetTargetSP(), condition.GetText(),
exe_ctx.GetFrameRef(), lldb::eNoDynamicValues,
>From b726cd7e4b85e00efb34f41e932c03b9c83b407a Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Thu, 24 Sep 2026 22:23:58 +0500
Subject: [PATCH 6/7] Add target.breakpoints-condition-mode setting
---
lldb/include/lldb/Breakpoint/StopCondition.h | 2 +-
.../Interpreter/CommandOptionArgumentTable.h | 4 +-
lldb/include/lldb/Target/Target.h | 1 +
lldb/include/lldb/lldb-enumerations.h | 3 +-
lldb/source/Breakpoint/BreakpointLocation.cpp | 11 ++++-
.../Commands/CommandObjectBreakpoint.cpp | 2 +-
lldb/source/Commands/Options.td | 5 ++-
lldb/source/Target/Target.cpp | 8 ++++
lldb/source/Target/TargetProperties.td | 6 +++
.../TestBreakpointConditions.py | 41 +++++++++++--------
10 files changed, 58 insertions(+), 25 deletions(-)
diff --git a/lldb/include/lldb/Breakpoint/StopCondition.h b/lldb/include/lldb/Breakpoint/StopCondition.h
index ff21767f4e927..aad27c07e0ccb 100644
--- a/lldb/include/lldb/Breakpoint/StopCondition.h
+++ b/lldb/include/lldb/Breakpoint/StopCondition.h
@@ -55,7 +55,7 @@ class StopCondition {
/// The evaluation mode for this condition.
lldb::BreakpointConditionMode m_condition_mode =
- lldb::eBreakpointConditionModeDWIM;
+ lldb::eBreakpointConditionModeDefault;
};
} // namespace lldb_private
diff --git a/lldb/include/lldb/Interpreter/CommandOptionArgumentTable.h b/lldb/include/lldb/Interpreter/CommandOptionArgumentTable.h
index ad4210066626f..1fa90a1313b9c 100644
--- a/lldb/include/lldb/Interpreter/CommandOptionArgumentTable.h
+++ b/lldb/include/lldb/Interpreter/CommandOptionArgumentTable.h
@@ -245,7 +245,7 @@ static constexpr OptionEnumValueElement g_completion_type[] = {
static constexpr OptionEnumValueElement g_breakpoint_condition_mode[] = {
{lldb::eBreakpointConditionModeDIL, "dil",
- "Use Data Inspection Language to evaluate the condition."},
+ "Use Data Inspection Language (DIL) to evaluate the condition."},
{lldb::eBreakpointConditionModeExpr, "expr",
"Use UserExpression to evaluate the condition."},
{lldb::eBreakpointConditionModeDWIM, "dwim",
@@ -271,7 +271,7 @@ static constexpr CommandObject::ArgumentTableEntry g_argument_table[] = {
{ lldb::eArgTypeAliasOptions, "options-for-aliased-command", lldb::CompletionType::eNoCompletion, {}, { nullptr, false }, "Command options to be used as part of an alias (abbreviation) definition. (See 'help commands alias' for more information.)" },
{ lldb::eArgTypeArchitecture, "arch", lldb::eArchitectureCompletion, {}, { arch_helper, true }, "The architecture name, e.g. i386 or x86_64." },
{ lldb::eArgTypeBoolean, "boolean", lldb::CompletionType::eNoCompletion, {}, { nullptr, false }, "A Boolean value: 'true' or 'false'" },
- { lldb::eArgTypeBreakpointConditionMode, "condition-mode", lldb::CompletionType::eNoCompletion, g_breakpoint_condition_mode, { nullptr, false }, "Specifies the mode to use when evaluating a breakpoint's condition expression." },
+ { lldb::eArgTypeBreakpointConditionMode, "condition-mode", lldb::CompletionType::eNoCompletion, g_breakpoint_condition_mode, { nullptr, false }, "Specifies the mode to use when evaluating the condition expression of breakpoints." },
{ lldb::eArgTypeBreakpointID, "breakpt-id", lldb::CompletionType::eNoCompletion, {}, { BreakpointIDHelpTextCallback, false }, nullptr },
{ lldb::eArgTypeBreakpointIDRange, "breakpt-id-list", lldb::CompletionType::eNoCompletion, {}, { BreakpointIDRangeHelpTextCallback, false }, nullptr },
{ lldb::eArgTypeBreakpointName, "breakpoint-name", lldb::eBreakpointNameCompletion, {}, { BreakpointNameHelpTextCallback, false }, nullptr },
diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h
index 0b4290ad4a33d..e0a864f1c80a3 100644
--- a/lldb/include/lldb/Target/Target.h
+++ b/lldb/include/lldb/Target/Target.h
@@ -227,6 +227,7 @@ class TargetProperties : public Properties {
void SetStandardErrorPath(const char *path) = delete;
bool GetBreakpointsConsultPlatformAvoidList();
+ lldb::BreakpointConditionMode GetBreakpointsConditionMode() const;
SourceLanguage GetLanguage() const;
diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h
index af060cd596aea..d163e052d61ad 100644
--- a/lldb/include/lldb/lldb-enumerations.h
+++ b/lldb/include/lldb/lldb-enumerations.h
@@ -1636,7 +1636,8 @@ constexpr unsigned BreakpointResolverAllResolversMask =
enum BreakpointConditionMode {
eBreakpointConditionModeDIL,
eBreakpointConditionModeExpr,
- eBreakpointConditionModeDWIM
+ eBreakpointConditionModeDWIM,
+ eBreakpointConditionModeDefault
};
} // namespace lldb
diff --git a/lldb/source/Breakpoint/BreakpointLocation.cpp b/lldb/source/Breakpoint/BreakpointLocation.cpp
index 4061f2861b6f3..cb61855788f6d 100644
--- a/lldb/source/Breakpoint/BreakpointLocation.cpp
+++ b/lldb/source/Breakpoint/BreakpointLocation.cpp
@@ -234,9 +234,16 @@ bool BreakpointLocation::ConditionSaysStop(ExecutionContext &exe_ctx,
return false;
}
+ // If the condition evaluation mode for this breakpoint is not specified,
+ // use the value from the target setting.
+ lldb::BreakpointConditionMode condition_mode =
+ condition.GetMode() != lldb::eBreakpointConditionModeDefault
+ ? condition.GetMode()
+ : GetTarget().GetBreakpointsConditionMode();
+
// Attempt to parse the condition using Data Inspection Language (DIL).
if (condition.GetHash() != m_condition_hash && exe_ctx.HasFrameScope() &&
- condition.GetMode() != lldb::eBreakpointConditionModeExpr) {
+ condition_mode != lldb::eBreakpointConditionModeExpr) {
// Lex the expression.
auto lex_or_err = dil::DILLexer::Create(condition.GetText(), eDILModeFull);
if (lex_or_err) {
@@ -289,7 +296,7 @@ bool BreakpointLocation::ConditionSaysStop(ExecutionContext &exe_ctx,
m_condition_hash = 0;
}
// DIL evaluation failed, trigger the breakpoint and return the error.
- if (condition.GetMode() == lldb::eBreakpointConditionModeDIL)
+ if (condition_mode == lldb::eBreakpointConditionModeDIL)
return true;
// The condition evaluation proceeds to UserExpression here.
diff --git a/lldb/source/Commands/CommandObjectBreakpoint.cpp b/lldb/source/Commands/CommandObjectBreakpoint.cpp
index 7d642d514cf8c..507568f8eee49 100644
--- a/lldb/source/Commands/CommandObjectBreakpoint.cpp
+++ b/lldb/source/Commands/CommandObjectBreakpoint.cpp
@@ -205,7 +205,7 @@ class lldb_private::BreakpointOptionGroup : public OptionGroup {
auto condition_mode =
static_cast<BreakpointConditionMode>(OptionArgParser::ToOptionEnum(
option_arg, GetDefinitions()[option_idx].enum_values,
- lldb::eBreakpointConditionModeDWIM, error));
+ lldb::eBreakpointConditionModeDefault, error));
m_bp_opts.GetCondition().SetMode(condition_mode);
} break;
default:
diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td
index 16e5b6292b3a7..66f54fa7e6493 100644
--- a/lldb/source/Commands/Options.td
+++ b/lldb/source/Commands/Options.td
@@ -143,13 +143,14 @@ let Command = "breakpoint modify" in {
Desc<"The breakpoint stops only if this ${c}ondition expression "
"evaluates to true. By default, DIL is used to evaluate the "
"expression, and if it fails, the evaluation falls back to "
- "UserExpression. Use -Z <condition-mode> to change this behavior.">;
+ "UserExpression. Use -Z (--condition-mode) to change this behavior.">;
def breakpoint_modify_condition_mode
: Option<"condition-mode", "Z">,
Group<1>,
EnumArg<"BreakpointConditionMode">,
Desc<"Specifies the mode to use when evaluating the breakpoint's "
- "condition expression. Default: dwim.">;
+ "condition expression. The default value can be set by "
+ "`target.breakpoints-condition-mode` setting.">;
def breakpoint_modify_condition_language
: Option<"condition-language", "Y">,
Group<1>,
diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp
index bff77dbc8b993..6b517b6bc1c43 100644
--- a/lldb/source/Target/Target.cpp
+++ b/lldb/source/Target/Target.cpp
@@ -37,6 +37,7 @@
#include "lldb/Host/PosixApi.h"
#include "lldb/Host/StreamFile.h"
#include "lldb/Interpreter/CommandInterpreter.h"
+#include "lldb/Interpreter/CommandOptionArgumentTable.h"
#include "lldb/Interpreter/CommandReturnObject.h"
#include "lldb/Interpreter/Interfaces/ScriptedBreakpointInterface.h"
#include "lldb/Interpreter/Interfaces/ScriptedHookInterface.h"
@@ -5779,6 +5780,13 @@ bool TargetProperties::GetBreakpointsConsultPlatformAvoidList() {
idx, g_target_properties[idx].default_uint_value != 0);
}
+BreakpointConditionMode TargetProperties::GetBreakpointsConditionMode() const {
+ const uint32_t idx = ePropertyBreakpointsConditionMode;
+ return GetPropertyAtIndexAs<BreakpointConditionMode>(
+ idx, static_cast<BreakpointConditionMode>(
+ g_target_properties[idx].default_uint_value));
+}
+
bool TargetProperties::GetUseHexImmediates() const {
const uint32_t idx = ePropertyUseHexImmediates;
return GetPropertyAtIndexAs<bool>(
diff --git a/lldb/source/Target/TargetProperties.td b/lldb/source/Target/TargetProperties.td
index 5474607bbf838..1100b2b907daf 100644
--- a/lldb/source/Target/TargetProperties.td
+++ b/lldb/source/Target/TargetProperties.td
@@ -117,6 +117,12 @@ let Definition = "target", Path = "target" in {
def BreakpointUseAvoidList: Property<"breakpoints-use-platform-avoid-list", "Boolean">,
DefaultTrue,
Desc<"Consult the platform module avoid list when setting non-module specific breakpoints.">;
+ def BreakpointsConditionMode: Property<"breakpoints-condition-mode", "Enum">,
+ DefaultEnumValue<"eBreakpointConditionModeDWIM">,
+ EnumValues<"OptionEnumValues(g_breakpoint_condition_mode)">,
+ Desc<"Specifies the default mode to use when evaluating the condition expression "
+ "of breakpoints. Default value: dwim. Use `help condition-mode` to list the modes. "
+ "Can be overridden by breakpoint command option -Z (--condition-mode).">;
def Arg0: Property<"arg0", "String">,
DefaultStringValue<"">,
Desc<"The first argument passed to the program in the argument array which can be different from the executable itself.">;
diff --git a/lldb/test/API/functionalities/breakpoint/breakpoint_conditions/TestBreakpointConditions.py b/lldb/test/API/functionalities/breakpoint/breakpoint_conditions/TestBreakpointConditions.py
index 4672e2d08fd4c..34347bdc3fd70 100644
--- a/lldb/test/API/functionalities/breakpoint/breakpoint_conditions/TestBreakpointConditions.py
+++ b/lldb/test/API/functionalities/breakpoint/breakpoint_conditions/TestBreakpointConditions.py
@@ -270,25 +270,30 @@ def breakpoint_conditions_with_DIL(self):
"""Test evaluating breakpoint conditions with Data Inspection Language."""
exe = self.getBuildArtifact("a.out")
self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
+ self.runCmd("settings set target.experimental.use-DIL true")
- def break_on_symbol_with_condition(symbol, condition, mode):
+ def break_on_symbol_with_condition(symbol, condition, mode=""):
+ if mode:
+ mode = "-Z {}".format(mode)
lldbutil.run_break_set_by_symbol(
self,
symbol,
- extra_options=" -c '{}' -Z {} -Y C++".format(condition, mode),
+ extra_options=" -c '{}' -Y C++ {}".format(condition, mode),
num_expected_locations=1,
sym_exact=True,
)
# Create a breakpoint with a valid condition.
- break_on_symbol_with_condition("a", "val == 1", "dwim")
+ break_on_symbol_with_condition("a", "val == 1")
# Create a breakpoint with a condition where DIL fails during lexing.
- break_on_symbol_with_condition("b", "val # 1", "dwim")
+ break_on_symbol_with_condition("b", "val # 1")
# Create a breakpoint with a condition where DIL fails during parsing.
- break_on_symbol_with_condition("b", "val == ?", "dwim")
+ break_on_symbol_with_condition("b", "val == ?")
# Create a breakpoint with a condition where DIL fails during evaluating.
- break_on_symbol_with_condition("b", "val == no_such_variable", "dwim")
- # Create a breakpoint with a valid condition where DIL fails during evaluating.
+ break_on_symbol_with_condition("b", "val == no_such_variable")
+ # Create a breakpoint with a valid condition where DIL fails during evaluating
+ # to test various condition evaluation modes.
+ break_on_symbol_with_condition("c", "val == static_cast<int>(1.0)")
break_on_symbol_with_condition("c", "val == static_cast<int>(1.0)", "dil")
break_on_symbol_with_condition("c", "val == static_cast<int>(1.0)", "expr")
break_on_symbol_with_condition("c", "val == static_cast<int>(1.0)", "dwim")
@@ -300,20 +305,14 @@ def break_on_symbol_with_condition(symbol, condition, mode):
self.runCmd("log enable -f '%s' lldb break" % (log_file))
# Run the program until breakpoint "a"
+ self.runCmd("settings set target.breakpoints-condition-mode dwim")
self.runCmd("run", RUN_SUCCEEDED)
- # Continue until breakpoint "b"
- self.runCmd("continue")
- # Continue until breakpoint "c"
- self.runCmd("continue")
-
- self.expect("process status", PROCESS_STOPPED, patterns=["Process .* stopped"])
-
- self.filecheck_log(log_file, __file__)
-
## Check the breakpoint "a" was evaluated successfully by DIL.
# CHECK: DIL successfully parsed the condition: val == 1
# CHECK: Condition successfully evaluated by DIL, result is true.
+ ## Continue until breakpoint "b"
+ self.runCmd("continue")
## Check that when DIL's lexer, parser and interpreter fail at breakpoint "b",
## the evaluation falls back to UserExpression, which should also fail.
## condition: "val # 1"
@@ -327,6 +326,13 @@ def break_on_symbol_with_condition(symbol, condition, mode):
# CHECK: Evaluating condition with DIL failed
# CHECK: error: <user expression
+ # Change the default mode and continue until breakpoint "c"
+ self.runCmd("settings set target.breakpoints-condition-mode expr")
+ self.runCmd("continue")
+ ## Check that "target.breakpoints-condition-mode" setting changed the default
+ ## mode to "expr" and DIL is not called.
+ # CHECK-NOT: DIL
+ # CHECK: Condition successfully evaluated by UserExpression, result is true
## Check that when DIL fails evaluating at breakpoint "c" with "dil" mode,
## the evaluation does not fall back to UserExpression.
# CHECK: Evaluating condition with DIL failed
@@ -341,4 +347,7 @@ def break_on_symbol_with_condition(symbol, condition, mode):
# CHECK: Evaluating condition with DIL failed
# CHECK: Condition successfully evaluated by UserExpression, result is true.
+ self.expect("process status", PROCESS_STOPPED, patterns=["Process .* stopped"])
+ self.filecheck_log(log_file, __file__)
+
self.runCmd("process kill")
>From a4e937e2a7ff76904729c25ed0564f9c010f384f Mon Sep 17 00:00:00 2001
From: Ilia Kuklin <ikuklin at accesssoftek.com>
Date: Thu, 24 Sep 2026 23:59:29 +0500
Subject: [PATCH 7/7] Add SB API Get/SetConditionMode
---
lldb/include/lldb/API/SBBreakpoint.h | 4 ++
lldb/include/lldb/Breakpoint/Breakpoint.h | 1 +
lldb/source/API/SBBreakpoint.cpp | 23 ++++++
lldb/source/Breakpoint/Breakpoint.cpp | 4 ++
.../TestBreakpointConditions.py | 72 ++++++++++---------
5 files changed, 71 insertions(+), 33 deletions(-)
diff --git a/lldb/include/lldb/API/SBBreakpoint.h b/lldb/include/lldb/API/SBBreakpoint.h
index 95c32fbb583bc..cc98a10a78f05 100644
--- a/lldb/include/lldb/API/SBBreakpoint.h
+++ b/lldb/include/lldb/API/SBBreakpoint.h
@@ -77,6 +77,10 @@ class LLDB_API SBBreakpoint {
const char *GetCondition();
+ void SetConditionMode(lldb::BreakpointConditionMode mode);
+
+ lldb::BreakpointConditionMode GetConditionMode();
+
void SetAutoContinue(bool auto_continue);
bool GetAutoContinue();
diff --git a/lldb/include/lldb/Breakpoint/Breakpoint.h b/lldb/include/lldb/Breakpoint/Breakpoint.h
index 14d3b17d272c7..65e23bfe03603 100644
--- a/lldb/include/lldb/Breakpoint/Breakpoint.h
+++ b/lldb/include/lldb/Breakpoint/Breakpoint.h
@@ -433,6 +433,7 @@ class Breakpoint : public std::enable_shared_from_this<Breakpoint>,
/// Return the breakpoint condition.
const StopCondition &GetCondition() const;
+ StopCondition &GetCondition();
// The next section are various utility functions.
diff --git a/lldb/source/API/SBBreakpoint.cpp b/lldb/source/API/SBBreakpoint.cpp
index 75d54cabfed22..d5be88803fe0a 100644
--- a/lldb/source/API/SBBreakpoint.cpp
+++ b/lldb/source/API/SBBreakpoint.cpp
@@ -298,6 +298,29 @@ const char *SBBreakpoint::GetCondition() {
return ConstString(cond.GetText()).GetCString();
}
+void SBBreakpoint::SetConditionMode(lldb::BreakpointConditionMode mode) {
+ LLDB_INSTRUMENT_VA(this, mode);
+
+ BreakpointSP bkpt_sp = GetSP();
+ if (bkpt_sp) {
+ TargetAPIMutex api_lock = bkpt_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
+ bkpt_sp->GetCondition().SetMode(mode);
+ }
+}
+
+lldb::BreakpointConditionMode SBBreakpoint::GetConditionMode() {
+ LLDB_INSTRUMENT_VA(this);
+
+ BreakpointSP bkpt_sp = GetSP();
+ if (bkpt_sp) {
+ TargetAPIMutex api_lock = bkpt_sp->GetTarget().GetAPIMutex();
+ std::lock_guard<TargetAPIMutex> guard(api_lock);
+ return bkpt_sp->GetCondition().GetMode();
+ }
+ return BreakpointConditionMode::eBreakpointConditionModeDefault;
+}
+
void SBBreakpoint::SetAutoContinue(bool auto_continue) {
LLDB_INSTRUMENT_VA(this, auto_continue);
diff --git a/lldb/source/Breakpoint/Breakpoint.cpp b/lldb/source/Breakpoint/Breakpoint.cpp
index 9b3cf4fdd787b..ad2d7479b2eaf 100644
--- a/lldb/source/Breakpoint/Breakpoint.cpp
+++ b/lldb/source/Breakpoint/Breakpoint.cpp
@@ -482,6 +482,10 @@ const StopCondition &Breakpoint::GetCondition() const {
return m_options.GetCondition();
}
+StopCondition &Breakpoint::GetCondition() {
+ return m_options.GetCondition();
+}
+
// This function is used when "baton" doesn't need to be freed
void Breakpoint::SetCallback(BreakpointHitCallback callback, void *baton,
bool is_synchronous) {
diff --git a/lldb/test/API/functionalities/breakpoint/breakpoint_conditions/TestBreakpointConditions.py b/lldb/test/API/functionalities/breakpoint/breakpoint_conditions/TestBreakpointConditions.py
index 34347bdc3fd70..7bee800692dcf 100644
--- a/lldb/test/API/functionalities/breakpoint/breakpoint_conditions/TestBreakpointConditions.py
+++ b/lldb/test/API/functionalities/breakpoint/breakpoint_conditions/TestBreakpointConditions.py
@@ -270,6 +270,7 @@ def breakpoint_conditions_with_DIL(self):
"""Test evaluating breakpoint conditions with Data Inspection Language."""
exe = self.getBuildArtifact("a.out")
self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
+ target = self.dbg.CreateTarget(exe)
self.runCmd("settings set target.experimental.use-DIL true")
def break_on_symbol_with_condition(symbol, condition, mode=""):
@@ -283,69 +284,74 @@ def break_on_symbol_with_condition(symbol, condition, mode=""):
sym_exact=True,
)
- # Create a breakpoint with a valid condition.
- break_on_symbol_with_condition("a", "val == 1")
- # Create a breakpoint with a condition where DIL fails during lexing.
- break_on_symbol_with_condition("b", "val # 1")
- # Create a breakpoint with a condition where DIL fails during parsing.
- break_on_symbol_with_condition("b", "val == ?")
- # Create a breakpoint with a condition where DIL fails during evaluating.
- break_on_symbol_with_condition("b", "val == no_such_variable")
- # Create a breakpoint with a valid condition where DIL fails during evaluating
- # to test various condition evaluation modes.
- break_on_symbol_with_condition("c", "val == static_cast<int>(1.0)")
- break_on_symbol_with_condition("c", "val == static_cast<int>(1.0)", "dil")
- break_on_symbol_with_condition("c", "val == static_cast<int>(1.0)", "expr")
- break_on_symbol_with_condition("c", "val == static_cast<int>(1.0)", "dwim")
-
# Enable logging
log_file = self.getBuildArtifact("log-file.txt")
if os.path.exists(log_file):
os.remove(log_file)
self.runCmd("log enable -f '%s' lldb break" % (log_file))
- # Run the program until breakpoint "a"
- self.runCmd("settings set target.breakpoints-condition-mode dwim")
- self.runCmd("run", RUN_SUCCEEDED)
- ## Check the breakpoint "a" was evaluated successfully by DIL.
+ ## Check that breakpoint "a" is successfully evaluated by DIL.
# CHECK: DIL successfully parsed the condition: val == 1
# CHECK: Condition successfully evaluated by DIL, result is true.
+ break_on_symbol_with_condition("a", "val == 1")
- ## Continue until breakpoint "b"
- self.runCmd("continue")
- ## Check that when DIL's lexer, parser and interpreter fail at breakpoint "b",
- ## the evaluation falls back to UserExpression, which should also fail.
- ## condition: "val # 1"
+ ## Run the program until breakpoint "a"
+ self.runCmd("settings set target.breakpoints-condition-mode dwim")
+ self.runCmd("run", RUN_SUCCEEDED)
+
+ ## Check that when DIL's lexer, parser or interpreter fail,
+ ## the evaluation falls back to UserExpression.
# CHECK: Lexing condition with DIL failed
# CHECK: error: <user expression
- ## condition: "val == ?"
+ break_on_symbol_with_condition("b", "val # 1")
# CHECK: Parsing condition with DIL failed
# CHECK: error: <user expression
- ## condition: "val == no_such_variable"
+ break_on_symbol_with_condition("b", "val == ?")
# CHECK: DIL successfully parsed the condition
# CHECK: Evaluating condition with DIL failed
# CHECK: error: <user expression
+ break_on_symbol_with_condition("b", "val == no_such_variable")
- # Change the default mode and continue until breakpoint "c"
- self.runCmd("settings set target.breakpoints-condition-mode expr")
+ # Continue until breakpoint "b"
self.runCmd("continue")
- ## Check that "target.breakpoints-condition-mode" setting changed the default
+
+ ## Create breakpoints with a valid condition where DIL fails during evaluating
+ ## to test various condition evaluation modes.
+ ## Check that "target.breakpoints-condition-mode" setting changes the default
## mode to "expr" and DIL is not called.
# CHECK-NOT: DIL
# CHECK: Condition successfully evaluated by UserExpression, result is true
- ## Check that when DIL fails evaluating at breakpoint "c" with "dil" mode,
+ self.runCmd("settings set target.breakpoints-condition-mode expr")
+ break_on_symbol_with_condition("c", "val == static_cast<int>(1.0)")
+ ## Check that when condition evaluation fails in "dil" mode,
## the evaluation does not fall back to UserExpression.
# CHECK: Evaluating condition with DIL failed
# CHECK: Error evaluating condition
- ## Check that the breakpoint "c" with "expr" mode was not attempted by DIL
- ## and was successfully evaluated by UserExpression.
+ break_on_symbol_with_condition("c", "val == static_cast<int>(1.0)", "dil")
+ ## Check that condition evaluation in "expr" mode does not attempt DIL
+ ## and successfully uses UserExpression.
# CHECK-NOT: DIL
# CHECK: Condition successfully evaluated by UserExpression, result is true
- ## Check that when DIL fails evaluating at breakpoint "c" with "dwim" mode,
+ break_on_symbol_with_condition("c", "val == static_cast<int>(1.0)", "expr")
+ ## Check that when DIL fails during condition evaluation in "dwim" mode,
## the evaluation falls back to UserExpression and succeeds.
# CHECK: DIL successfully parsed the condition: val == static_cast<int>(1.0)
# CHECK: Evaluating condition with DIL failed
# CHECK: Condition successfully evaluated by UserExpression, result is true.
+ break_on_symbol_with_condition("c", "val == static_cast<int>(1.0)", "dwim")
+ ## Check that the breakpoint condition mode is set to DIL via SB API,
+ ## and UserExpression is not called.
+ # CHECK: Evaluating condition with DIL failed
+ # CHECK: Error evaluating condition
+ breakpoint = target.BreakpointCreateByName("c")
+ breakpoint.SetCondition("val == static_cast<int>(1.0)")
+ breakpoint.SetConditionMode(lldb.eBreakpointConditionModeDIL)
+ self.assertEqual(
+ breakpoint.GetConditionMode(), lldb.eBreakpointConditionModeDIL
+ )
+
+ # Continue until breakpoint "c"
+ self.runCmd("continue")
self.expect("process status", PROCESS_STOPPED, patterns=["Process .* stopped"])
self.filecheck_log(log_file, __file__)
More information about the lldb-commits
mailing list