[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