aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJonas Devlieghere <jonas@devlieghere.com>2025-07-10 15:24:27 -0700
committerGitHub <noreply@github.com>2025-07-10 15:24:27 -0700
commitf28a497a06c2d9202638d753e1cd2e247814d180 (patch)
tree5599ddef580f55fbf72b8f47f2ad8a516a2cf319
parent4859b92b7f4e0365517acd464cec29721f469461 (diff)
downloadllvm-f28a497a06c2d9202638d753e1cd2e247814d180.zip
llvm-f28a497a06c2d9202638d753e1cd2e247814d180.tar.gz
llvm-f28a497a06c2d9202638d753e1cd2e247814d180.tar.bz2
[lldb] Support specifying a language for breakpoint conditions (#147603)
LLDB breakpoint conditions take an expression that's evaluated using the language of the code where the breakpoint is located. Users have asked to have an option to tell it to evaluate the expression in a specific language. This is feature is especially helpful for Swift, for example for a condition based on the value in memory at an offset from a register. Such a condition is pretty difficult to write in Swift, but easy in C. This PR adds a new argument (-Y) to specify the language of the condition expression. We can't reuse the current -L option, since you might want to break on only Swift symbols, but run a C expression there as per the example above. rdar://146119507
-rw-r--r--lldb/include/lldb/Breakpoint/Breakpoint.h14
-rw-r--r--lldb/include/lldb/Breakpoint/BreakpointLocation.h12
-rw-r--r--lldb/include/lldb/Breakpoint/BreakpointOptions.h20
-rw-r--r--lldb/include/lldb/Breakpoint/StopCondition.h55
-rw-r--r--lldb/source/API/SBBreakpoint.cpp4
-rw-r--r--lldb/source/API/SBBreakpointLocation.cpp4
-rw-r--r--lldb/source/API/SBBreakpointName.cpp5
-rw-r--r--lldb/source/Breakpoint/Breakpoint.cpp8
-rw-r--r--lldb/source/Breakpoint/BreakpointLocation.cpp37
-rw-r--r--lldb/source/Breakpoint/BreakpointOptions.cpp52
-rw-r--r--lldb/source/Commands/CommandObjectBreakpoint.cpp17
-rw-r--r--lldb/source/Commands/Options.td6
-rw-r--r--lldb/source/Target/StopInfo.cpp4
-rw-r--r--lldb/test/API/functionalities/breakpoint/breakpoint_conditions/TestBreakpointConditions.py29
-rw-r--r--lldb/test/Shell/Breakpoint/condition-lang.test5
15 files changed, 176 insertions, 96 deletions
diff --git a/lldb/include/lldb/Breakpoint/Breakpoint.h b/lldb/include/lldb/Breakpoint/Breakpoint.h
index b200a1e..26a5e90 100644
--- a/lldb/include/lldb/Breakpoint/Breakpoint.h
+++ b/lldb/include/lldb/Breakpoint/Breakpoint.h
@@ -397,16 +397,12 @@ public:
/// Set the breakpoint's condition.
///
/// \param[in] condition
- /// The condition expression to evaluate when the breakpoint is hit.
- /// Pass in nullptr to clear the condition.
- void SetCondition(const char *condition);
+ /// The condition to evaluate when the breakpoint is hit.
+ /// Pass in an empty condition to clear the condition.
+ void SetCondition(StopCondition condition);
- /// Return a pointer to the text of the condition expression.
- ///
- /// \return
- /// A pointer to the condition expression text, or nullptr if no
- // condition has been set.
- const char *GetConditionText() const;
+ /// Return the breakpoint condition.
+ const StopCondition &GetCondition() const;
// The next section are various utility functions.
diff --git a/lldb/include/lldb/Breakpoint/BreakpointLocation.h b/lldb/include/lldb/Breakpoint/BreakpointLocation.h
index ce3a21f..ab2e5e1 100644
--- a/lldb/include/lldb/Breakpoint/BreakpointLocation.h
+++ b/lldb/include/lldb/Breakpoint/BreakpointLocation.h
@@ -128,15 +128,11 @@ public:
/// Set the breakpoint location's condition.
///
/// \param[in] condition
- /// The condition expression to evaluate when the breakpoint is hit.
- void SetCondition(const char *condition);
+ /// The condition to evaluate when the breakpoint is hit.
+ void SetCondition(StopCondition condition);
- /// Return a pointer to the text of the condition expression.
- ///
- /// \return
- /// A pointer to the condition expression text, or nullptr if no
- // condition has been set.
- const char *GetConditionText(size_t *hash = nullptr) const;
+ /// Return the breakpoint condition.
+ const StopCondition &GetCondition() const;
bool ConditionSaysStop(ExecutionContext &exe_ctx, Status &error);
diff --git a/lldb/include/lldb/Breakpoint/BreakpointOptions.h b/lldb/include/lldb/Breakpoint/BreakpointOptions.h
index 7bf5457..2f73473 100644
--- a/lldb/include/lldb/Breakpoint/BreakpointOptions.h
+++ b/lldb/include/lldb/Breakpoint/BreakpointOptions.h
@@ -12,6 +12,7 @@
#include <memory>
#include <string>
+#include "lldb/Breakpoint/StopCondition.h"
#include "lldb/Utility/Baton.h"
#include "lldb/Utility/Flags.h"
#include "lldb/Utility/StringList.h"
@@ -245,18 +246,15 @@ public:
const Baton *GetBaton() const;
// Condition
- /// Set the breakpoint option's condition.
+ /// Set the breakpoint stop condition.
///
/// \param[in] condition
- /// The condition expression to evaluate when the breakpoint is hit.
- void SetCondition(const char *condition);
+ /// The condition to evaluate when the breakpoint is hit.
+ void SetCondition(StopCondition condition);
- /// Return a pointer to the text of the condition expression.
- ///
- /// \return
- /// A pointer to the condition expression text, or nullptr if no
- // condition has been set.
- const char *GetConditionText(size_t *hash = nullptr) const;
+ /// Return the breakpoint condition.
+ const StopCondition &GetCondition() const;
+ StopCondition &GetCondition();
// Enabled/Ignore Count
@@ -390,9 +388,7 @@ private:
/// Thread for which this breakpoint will stop.
std::unique_ptr<ThreadSpec> m_thread_spec_up;
/// The condition to test.
- std::string m_condition_text;
- /// Its hash, so that locations know when the condition is updated.
- size_t m_condition_text_hash;
+ StopCondition m_condition;
/// If set, inject breakpoint condition into process.
bool m_inject_condition;
/// If set, auto-continue from breakpoint.
diff --git a/lldb/include/lldb/Breakpoint/StopCondition.h b/lldb/include/lldb/Breakpoint/StopCondition.h
new file mode 100644
index 0000000..485a615
--- /dev/null
+++ b/lldb/include/lldb/Breakpoint/StopCondition.h
@@ -0,0 +1,55 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_BREAKPOINT_STOPCONDITION_H
+#define LLDB_BREAKPOINT_STOPCONDITION_H
+
+#include "lldb/lldb-private.h"
+#include "llvm/ADT/StringRef.h"
+
+namespace lldb_private {
+
+class StopCondition {
+public:
+ StopCondition() = default;
+ StopCondition(std::string text,
+ lldb::LanguageType language = lldb::eLanguageTypeUnknown)
+ : m_language(language) {
+ SetText(std::move(text));
+ }
+
+ explicit operator bool() const { return !m_text.empty(); }
+
+ llvm::StringRef GetText() const { return m_text; }
+
+ void SetText(std::string text) {
+ static std::hash<std::string> hasher;
+ m_text = std::move(text);
+ m_hash = hasher(text);
+ }
+
+ size_t GetHash() const { return m_hash; }
+
+ lldb::LanguageType GetLanguage() const { return m_language; }
+
+ void SetLanguage(lldb::LanguageType language) { m_language = language; }
+
+private:
+ /// The condition to test.
+ std::string m_text;
+
+ /// Its hash, so that locations know when the condition is updated.
+ size_t m_hash = 0;
+
+ /// The language for this condition.
+ lldb::LanguageType m_language = lldb::eLanguageTypeUnknown;
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_BREAKPOINT_STOPCONDITION_H
diff --git a/lldb/source/API/SBBreakpoint.cpp b/lldb/source/API/SBBreakpoint.cpp
index 397afc1..07c0a2e 100644
--- a/lldb/source/API/SBBreakpoint.cpp
+++ b/lldb/source/API/SBBreakpoint.cpp
@@ -275,7 +275,7 @@ void SBBreakpoint::SetCondition(const char *condition) {
if (bkpt_sp) {
std::lock_guard<std::recursive_mutex> guard(
bkpt_sp->GetTarget().GetAPIMutex());
- bkpt_sp->SetCondition(condition);
+ bkpt_sp->SetCondition(StopCondition(condition));
}
}
@@ -288,7 +288,7 @@ const char *SBBreakpoint::GetCondition() {
std::lock_guard<std::recursive_mutex> guard(
bkpt_sp->GetTarget().GetAPIMutex());
- return ConstString(bkpt_sp->GetConditionText()).GetCString();
+ return ConstString(bkpt_sp->GetCondition().GetText()).GetCString();
}
void SBBreakpoint::SetAutoContinue(bool auto_continue) {
diff --git a/lldb/source/API/SBBreakpointLocation.cpp b/lldb/source/API/SBBreakpointLocation.cpp
index 479354a..e786435 100644
--- a/lldb/source/API/SBBreakpointLocation.cpp
+++ b/lldb/source/API/SBBreakpointLocation.cpp
@@ -160,7 +160,7 @@ void SBBreakpointLocation::SetCondition(const char *condition) {
if (loc_sp) {
std::lock_guard<std::recursive_mutex> guard(
loc_sp->GetTarget().GetAPIMutex());
- loc_sp->SetCondition(condition);
+ loc_sp->SetCondition(StopCondition(condition));
}
}
@@ -173,7 +173,7 @@ const char *SBBreakpointLocation::GetCondition() {
std::lock_guard<std::recursive_mutex> guard(
loc_sp->GetTarget().GetAPIMutex());
- return ConstString(loc_sp->GetConditionText()).GetCString();
+ return ConstString(loc_sp->GetCondition().GetText()).GetCString();
}
void SBBreakpointLocation::SetAutoContinue(bool auto_continue) {
diff --git a/lldb/source/API/SBBreakpointName.cpp b/lldb/source/API/SBBreakpointName.cpp
index 831260d..0b588c3 100644
--- a/lldb/source/API/SBBreakpointName.cpp
+++ b/lldb/source/API/SBBreakpointName.cpp
@@ -303,7 +303,7 @@ void SBBreakpointName::SetCondition(const char *condition) {
std::lock_guard<std::recursive_mutex> guard(
m_impl_up->GetTarget()->GetAPIMutex());
- bp_name->GetOptions().SetCondition(condition);
+ bp_name->GetOptions().SetCondition(StopCondition(condition));
UpdateName(*bp_name);
}
@@ -317,7 +317,8 @@ const char *SBBreakpointName::GetCondition() {
std::lock_guard<std::recursive_mutex> guard(
m_impl_up->GetTarget()->GetAPIMutex());
- return ConstString(bp_name->GetOptions().GetConditionText()).GetCString();
+ return ConstString(bp_name->GetOptions().GetCondition().GetText())
+ .GetCString();
}
void SBBreakpointName::SetAutoContinue(bool auto_continue) {
diff --git a/lldb/source/Breakpoint/Breakpoint.cpp b/lldb/source/Breakpoint/Breakpoint.cpp
index ec27a7d..d757bc4 100644
--- a/lldb/source/Breakpoint/Breakpoint.cpp
+++ b/lldb/source/Breakpoint/Breakpoint.cpp
@@ -440,13 +440,13 @@ const char *Breakpoint::GetQueueName() const {
return m_options.GetThreadSpecNoCreate()->GetQueueName();
}
-void Breakpoint::SetCondition(const char *condition) {
- m_options.SetCondition(condition);
+void Breakpoint::SetCondition(StopCondition condition) {
+ m_options.SetCondition(std::move(condition));
SendBreakpointChangedEvent(eBreakpointEventTypeConditionChanged);
}
-const char *Breakpoint::GetConditionText() const {
- return m_options.GetConditionText();
+const StopCondition &Breakpoint::GetCondition() const {
+ return m_options.GetCondition();
}
// This function is used when "baton" doesn't need to be freed
diff --git a/lldb/source/Breakpoint/BreakpointLocation.cpp b/lldb/source/Breakpoint/BreakpointLocation.cpp
index 7ac9c8f..443d4f5 100644
--- a/lldb/source/Breakpoint/BreakpointLocation.cpp
+++ b/lldb/source/Breakpoint/BreakpointLocation.cpp
@@ -203,14 +203,13 @@ void BreakpointLocation::ClearCallback() {
GetLocationOptions().ClearCallback();
}
-void BreakpointLocation::SetCondition(const char *condition) {
- GetLocationOptions().SetCondition(condition);
+void BreakpointLocation::SetCondition(StopCondition condition) {
+ GetLocationOptions().SetCondition(std::move(condition));
SendBreakpointLocationChangedEvent(eBreakpointEventTypeConditionChanged);
}
-const char *BreakpointLocation::GetConditionText(size_t *hash) const {
- return GetOptionsSpecifyingKind(BreakpointOptions::eCondition)
- .GetConditionText(hash);
+const StopCondition &BreakpointLocation::GetCondition() const {
+ return GetOptionsSpecifyingKind(BreakpointOptions::eCondition).GetCondition();
}
bool BreakpointLocation::ConditionSaysStop(ExecutionContext &exe_ctx,
@@ -219,10 +218,9 @@ bool BreakpointLocation::ConditionSaysStop(ExecutionContext &exe_ctx,
std::lock_guard<std::mutex> guard(m_condition_mutex);
- size_t condition_hash;
- const char *condition_text = GetConditionText(&condition_hash);
+ StopCondition condition = GetCondition();
- if (!condition_text) {
+ if (!condition) {
m_user_expression_sp.reset();
return false;
}
@@ -231,19 +229,22 @@ bool BreakpointLocation::ConditionSaysStop(ExecutionContext &exe_ctx,
DiagnosticManager diagnostics;
- if (condition_hash != m_condition_hash || !m_user_expression_sp ||
+ if (condition.GetHash() != m_condition_hash || !m_user_expression_sp ||
!m_user_expression_sp->IsParseCacheable() ||
!m_user_expression_sp->MatchesContext(exe_ctx)) {
- LanguageType language = eLanguageTypeUnknown;
- // See if we can figure out the language from the frame, otherwise use the
- // default language:
- CompileUnit *comp_unit = m_address.CalculateSymbolContextCompileUnit();
- if (comp_unit)
- language = comp_unit->GetLanguage();
+ LanguageType language = condition.GetLanguage();
+ if (language == lldb::eLanguageTypeUnknown) {
+ // See if we can figure out the language from the frame, otherwise use the
+ // default language:
+ if (CompileUnit *comp_unit =
+ m_address.CalculateSymbolContextCompileUnit())
+ language = comp_unit->GetLanguage();
+ }
m_user_expression_sp.reset(GetTarget().GetUserExpressionForLanguage(
- condition_text, llvm::StringRef(), language, Expression::eResultTypeAny,
- EvaluateExpressionOptions(), nullptr, error));
+ condition.GetText(), llvm::StringRef(), language,
+ Expression::eResultTypeAny, EvaluateExpressionOptions(), nullptr,
+ error));
if (error.Fail()) {
LLDB_LOGF(log, "Error getting condition expression: %s.",
error.AsCString());
@@ -262,7 +263,7 @@ bool BreakpointLocation::ConditionSaysStop(ExecutionContext &exe_ctx,
return true;
}
- m_condition_hash = condition_hash;
+ m_condition_hash = condition.GetHash();
}
// We need to make sure the user sees any parse errors in their condition, so
diff --git a/lldb/source/Breakpoint/BreakpointOptions.cpp b/lldb/source/Breakpoint/BreakpointOptions.cpp
index 08e48c4..b0b794f 100644
--- a/lldb/source/Breakpoint/BreakpointOptions.cpp
+++ b/lldb/source/Breakpoint/BreakpointOptions.cpp
@@ -106,8 +106,8 @@ const char *BreakpointOptions::g_option_names[(
BreakpointOptions::BreakpointOptions(bool all_flags_set)
: m_callback(nullptr), m_baton_is_command_baton(false),
m_callback_is_synchronous(false), m_enabled(true), m_one_shot(false),
- m_ignore_count(0), m_condition_text_hash(0), m_inject_condition(false),
- m_auto_continue(false), m_set_flags(0) {
+ m_ignore_count(0), m_inject_condition(false), m_auto_continue(false),
+ m_set_flags(0) {
if (all_flags_set)
m_set_flags.Set(~((Flags::ValueType)0));
}
@@ -117,11 +117,11 @@ BreakpointOptions::BreakpointOptions(const char *condition, bool enabled,
bool auto_continue)
: m_callback(nullptr), m_baton_is_command_baton(false),
m_callback_is_synchronous(false), m_enabled(enabled),
- m_one_shot(one_shot), m_ignore_count(ignore), m_condition_text_hash(0),
+ m_one_shot(one_shot), m_ignore_count(ignore), m_condition(condition),
m_inject_condition(false), m_auto_continue(auto_continue) {
m_set_flags.Set(eEnabled | eIgnoreCount | eOneShot | eAutoContinue);
if (condition && *condition != '\0') {
- SetCondition(condition);
+ SetCondition(StopCondition(condition));
}
}
@@ -135,8 +135,7 @@ BreakpointOptions::BreakpointOptions(const BreakpointOptions &rhs)
m_auto_continue(rhs.m_auto_continue), m_set_flags(rhs.m_set_flags) {
if (rhs.m_thread_spec_up != nullptr)
m_thread_spec_up = std::make_unique<ThreadSpec>(*rhs.m_thread_spec_up);
- m_condition_text = rhs.m_condition_text;
- m_condition_text_hash = rhs.m_condition_text_hash;
+ m_condition = rhs.m_condition;
}
// BreakpointOptions assignment operator
@@ -151,8 +150,7 @@ operator=(const BreakpointOptions &rhs) {
m_ignore_count = rhs.m_ignore_count;
if (rhs.m_thread_spec_up != nullptr)
m_thread_spec_up = std::make_unique<ThreadSpec>(*rhs.m_thread_spec_up);
- m_condition_text = rhs.m_condition_text;
- m_condition_text_hash = rhs.m_condition_text_hash;
+ m_condition = rhs.m_condition;
m_inject_condition = rhs.m_inject_condition;
m_auto_continue = rhs.m_auto_continue;
m_set_flags = rhs.m_set_flags;
@@ -187,13 +185,11 @@ void BreakpointOptions::CopyOverSetOptions(const BreakpointOptions &incoming)
if (incoming.m_set_flags.Test(eCondition))
{
// If we're copying over an empty condition, mark it as unset.
- if (incoming.m_condition_text.empty()) {
- m_condition_text.clear();
- m_condition_text_hash = 0;
+ if (!incoming.m_condition) {
+ m_condition = StopCondition();
m_set_flags.Clear(eCondition);
} else {
- m_condition_text = incoming.m_condition_text;
- m_condition_text_hash = incoming.m_condition_text_hash;
+ m_condition = incoming.m_condition;
m_set_flags.Set(eCondition);
}
}
@@ -363,7 +359,7 @@ StructuredData::ObjectSP BreakpointOptions::SerializeToStructuredData() {
m_ignore_count);
if (m_set_flags.Test(eCondition))
options_dict_sp->AddStringItem(GetKey(OptionNames::ConditionText),
- m_condition_text);
+ m_condition.GetText());
if (m_set_flags.Test(eCallback) && m_baton_is_command_baton) {
auto cmd_baton =
@@ -464,29 +460,21 @@ bool BreakpointOptions::GetCommandLineCallbacks(StringList &command_list) {
return true;
}
-void BreakpointOptions::SetCondition(const char *condition) {
- if (!condition || condition[0] == '\0') {
- condition = "";
+void BreakpointOptions::SetCondition(StopCondition condition) {
+ if (!condition)
m_set_flags.Clear(eCondition);
- }
else
m_set_flags.Set(eCondition);
- m_condition_text.assign(condition);
- std::hash<std::string> hasher;
- m_condition_text_hash = hasher(m_condition_text);
+ m_condition = std::move(condition);
}
-const char *BreakpointOptions::GetConditionText(size_t *hash) const {
- if (!m_condition_text.empty()) {
- if (hash)
- *hash = m_condition_text_hash;
-
- return m_condition_text.c_str();
- }
- return nullptr;
+const StopCondition &BreakpointOptions::GetCondition() const {
+ return m_condition;
}
+StopCondition &BreakpointOptions::GetCondition() { return m_condition; }
+
const ThreadSpec *BreakpointOptions::GetThreadSpecNoCreate() const {
return m_thread_spec_up.get();
}
@@ -555,10 +543,10 @@ void BreakpointOptions::GetDescription(Stream *s,
s->GetIndentLevel());
}
}
- if (!m_condition_text.empty()) {
+ if (m_condition) {
if (level != eDescriptionLevelBrief) {
s->EOL();
- s->Printf("Condition: %s\n", m_condition_text.c_str());
+ s->Printf("Condition: %s\n", m_condition.GetText().data());
}
}
}
@@ -652,5 +640,5 @@ void BreakpointOptions::Clear()
m_baton_is_command_baton = false;
m_callback_is_synchronous = false;
m_enabled = false;
- m_condition_text.clear();
+ m_condition = StopCondition();
}
diff --git a/lldb/source/Commands/CommandObjectBreakpoint.cpp b/lldb/source/Commands/CommandObjectBreakpoint.cpp
index 2440a7e..38ec375 100644
--- a/lldb/source/Commands/CommandObjectBreakpoint.cpp
+++ b/lldb/source/Commands/CommandObjectBreakpoint.cpp
@@ -72,7 +72,7 @@ public:
case 'c':
// Normally an empty breakpoint condition marks is as unset. But we need
// to say it was passed in.
- m_bp_opts.SetCondition(option_arg.str().c_str());
+ m_bp_opts.GetCondition().SetText(option_arg.str());
m_bp_opts.m_set_flags.Set(BreakpointOptions::eCondition);
break;
case 'C':
@@ -154,6 +154,21 @@ public:
m_bp_opts.GetThreadSpec()->SetIndex(thread_index);
}
} break;
+ case 'Y': {
+ LanguageType language = Language::GetLanguageTypeFromString(option_arg);
+
+ LanguageSet languages_for_expressions =
+ Language::GetLanguagesSupportingTypeSystemsForExpressions();
+ if (language == eLanguageTypeUnknown)
+ error = Status::FromError(CreateOptionParsingError(
+ option_arg, short_option, long_option, "invalid language"));
+ else if (!languages_for_expressions[language])
+ error = Status::FromError(
+ CreateOptionParsingError(option_arg, short_option, long_option,
+ "no expression support for language"));
+ else
+ m_bp_opts.GetCondition().SetLanguage(language);
+ } break;
default:
llvm_unreachable("Unimplemented option");
}
diff --git a/lldb/source/Commands/Options.td b/lldb/source/Commands/Options.td
index e543566..acb7410 100644
--- a/lldb/source/Commands/Options.td
+++ b/lldb/source/Commands/Options.td
@@ -95,6 +95,12 @@ let Command = "breakpoint modify" in {
def breakpoint_modify_condition : Option<"condition", "c">, Group<1>,
Arg<"Expression">, Desc<"The breakpoint stops only if this condition "
"expression evaluates to true.">;
+ 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.">;
def breakpoint_modify_auto_continue : Option<"auto-continue", "G">, Group<1>,
Arg<"Boolean">,
Desc<"The breakpoint will auto-continue after running its commands.">;
diff --git a/lldb/source/Target/StopInfo.cpp b/lldb/source/Target/StopInfo.cpp
index 3160446..19f89b8 100644
--- a/lldb/source/Target/StopInfo.cpp
+++ b/lldb/source/Target/StopInfo.cpp
@@ -465,7 +465,7 @@ protected:
// should stop, then we'll run the callback for the breakpoint. If
// the callback says we shouldn't stop that will win.
- if (bp_loc_sp->GetConditionText() == nullptr)
+ if (!bp_loc_sp->GetCondition())
actually_hit_any_locations = true;
else {
Status condition_error;
@@ -484,7 +484,7 @@ protected:
strm << "stopped due to an error evaluating condition of "
"breakpoint ";
bp_loc_sp->GetDescription(&strm, eDescriptionLevelBrief);
- strm << ": \"" << bp_loc_sp->GetConditionText() << "\"\n";
+ strm << ": \"" << bp_loc_sp->GetCondition().GetText() << "\"\n";
strm << err_str;
Debugger::ReportError(
diff --git a/lldb/test/API/functionalities/breakpoint/breakpoint_conditions/TestBreakpointConditions.py b/lldb/test/API/functionalities/breakpoint/breakpoint_conditions/TestBreakpointConditions.py
index 4e7a8cc..a4c9c49 100644
--- a/lldb/test/API/functionalities/breakpoint/breakpoint_conditions/TestBreakpointConditions.py
+++ b/lldb/test/API/functionalities/breakpoint/breakpoint_conditions/TestBreakpointConditions.py
@@ -19,6 +19,16 @@ class BreakpointConditionsTestCase(TestBase):
self.build()
self.breakpoint_conditions(inline=True)
+ def test_breakpoint_condition_and_run_command_language(self):
+ """Exercise breakpoint condition with 'breakpoint modify -c <expr> id'."""
+ self.build()
+ self.breakpoint_conditions(cpp=True)
+
+ def test_breakpoint_condition_inline_and_run_command_language(self):
+ """Exercise breakpoint condition inline with 'breakpoint set'."""
+ self.build()
+ self.breakpoint_conditions(inline=True, cpp=True)
+
@add_test_categories(["pyapi"])
def test_breakpoint_condition_and_python_api(self):
"""Use Python APIs to set breakpoint conditions."""
@@ -42,17 +52,24 @@ class BreakpointConditionsTestCase(TestBase):
"main.c", "// Find the line number of c's parent call here."
)
- def breakpoint_conditions(self, inline=False):
+ def breakpoint_conditions(self, inline=False, cpp=False):
"""Exercise breakpoint condition with 'breakpoint modify -c <expr> id'."""
exe = self.getBuildArtifact("a.out")
self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
+ if cpp:
+ condition = "&val != nullptr && val == 3"
+ cmd_args = " -c '{}' -Y c++".format(condition)
+ else:
+ condition = "val == 3"
+ cmd_args = "-c '{}'".format(condition)
+
if inline:
# Create a breakpoint by function name 'c' and set the condition.
lldbutil.run_break_set_by_symbol(
self,
"c",
- extra_options="-c 'val == 3'",
+ extra_options=cmd_args,
num_expected_locations=1,
sym_exact=True,
)
@@ -63,7 +80,7 @@ class BreakpointConditionsTestCase(TestBase):
)
# And set a condition on the breakpoint to stop on when 'val == 3'.
- self.runCmd("breakpoint modify -c 'val == 3' 1")
+ self.runCmd("breakpoint modify " + cmd_args + " 1")
# Now run the program.
self.runCmd("run", RUN_SUCCEEDED)
@@ -82,7 +99,11 @@ class BreakpointConditionsTestCase(TestBase):
self.expect(
"breakpoint list -f",
BREAKPOINT_HIT_ONCE,
- substrs=["resolved = 1", "Condition: val == 3", "hit count = 1"],
+ substrs=[
+ "resolved = 1",
+ "Condition: {}".format(condition),
+ "hit count = 1",
+ ],
)
# The frame #0 should correspond to main.c:36, the executable statement
diff --git a/lldb/test/Shell/Breakpoint/condition-lang.test b/lldb/test/Shell/Breakpoint/condition-lang.test
new file mode 100644
index 0000000..9a64bf4f
--- /dev/null
+++ b/lldb/test/Shell/Breakpoint/condition-lang.test
@@ -0,0 +1,5 @@
+RUN: not %lldb -b -o 'break set -n foo -c bar -Y bogus' 2>&1 | FileCheck %s --check-prefix INVALID
+INVALID: error: Invalid value ('bogus') for -Y (condition-language): invalid language
+
+RUN: not %lldb -b -o 'break set -n foo -c bar -Y python' 2>&1 | FileCheck %s --check-prefix NOEXPRSUPPORT
+NOEXPRSUPPORT: error: Invalid value ('python') for -Y (condition-language): no expression support for language