[Lldb-commits] [lldb] [lldb] add software watchpoints support (PR #151195)
via lldb-commits
lldb-commits at lists.llvm.org
Tue Jul 29 14:01:55 PDT 2025
https://github.com/dlav-sc updated https://github.com/llvm/llvm-project/pull/151195
>From 87d4e1e7b0c594d596751fca2bccb6ac20afe233 Mon Sep 17 00:00:00 2001
From: Daniil Avdeev <daniil.avdeev at syntacore.com>
Date: Mon, 10 Feb 2025 03:16:50 +0000
Subject: [PATCH 1/5] [lldb] add software watchpoints backend
This patch adds support for software watchpoints in LLDB.
This functionality has long been available in GDB, so it
is also necessary for LLDB.
All logic is implemented at the LLDB client level, as the
GDB remote protocol currently does not support packets
that establish software watchpoints. When software
watchpoints are present, LLDB will perform instruction
steps and check the watchpoints for hits.
To set a software watchpoints, use the -S flag:
wa s v -S local_variable
wa s e -S -- global_ptr
Note: For now, this patch doesn't extend lldb watchpoint
functionality. It simply provides the option to use software watchpoints
instead of hardware ones in the same scenarios. However, I plan to
extend this functionality in the future. For example, we could use
software watchpoints to monitor registers and other arbitrary
expressions that aren't placed in memory.
---
lldb/include/lldb/Breakpoint/Watchpoint.h | 189 ++++++++--
.../lldb/Interpreter/OptionGroupWatchpoint.h | 1 +
lldb/include/lldb/Target/Process.h | 2 +
lldb/include/lldb/Target/Target.h | 14 +-
lldb/include/lldb/Target/ThreadPlan.h | 1 +
.../lldb/Target/ThreadPlanStepInstruction.h | 8 +-
lldb/include/lldb/lldb-enumerations.h | 3 +
lldb/source/Breakpoint/Watchpoint.cpp | 343 ++++++++++++------
.../Commands/CommandObjectWatchpoint.cpp | 52 ++-
.../Interpreter/OptionGroupWatchpoint.cpp | 25 +-
.../Process/gdb-remote/ProcessGDBRemote.cpp | 57 +--
lldb/source/Target/Process.cpp | 34 ++
lldb/source/Target/StopInfo.cpp | 257 +++++--------
lldb/source/Target/Target.cpp | 260 +++++++++----
lldb/source/Target/Thread.cpp | 128 ++++++-
.../Target/ThreadPlanStepInstruction.cpp | 11 +-
16 files changed, 945 insertions(+), 440 deletions(-)
diff --git a/lldb/include/lldb/Breakpoint/Watchpoint.h b/lldb/include/lldb/Breakpoint/Watchpoint.h
index ca48a0114888a..d0a061634a910 100644
--- a/lldb/include/lldb/Breakpoint/Watchpoint.h
+++ b/lldb/include/lldb/Breakpoint/Watchpoint.h
@@ -15,6 +15,7 @@
#include "lldb/Breakpoint/StoppointSite.h"
#include "lldb/Breakpoint/WatchpointOptions.h"
#include "lldb/Symbol/CompilerType.h"
+#include "lldb/Target/Process.h"
#include "lldb/Target/Target.h"
#include "lldb/Utility/UserID.h"
#include "lldb/lldb-private.h"
@@ -58,37 +59,95 @@ class Watchpoint : public std::enable_shared_from_this<Watchpoint>,
const WatchpointEventData &operator=(const WatchpointEventData &) = delete;
};
+ // Make sure watchpoint is properly disabled and subsequently enabled while
+ // performing watchpoint actions.
+ class WatchpointSentry {
+ public:
+ WatchpointSentry(lldb::ProcessSP p_sp, lldb::WatchpointSP w_sp)
+ : process_sp(p_sp), watchpoint_sp(w_sp) {
+ lldbassert(process_sp && watchpoint_sp && "Ill-formed WatchpointSentry!");
+
+ constexpr bool notify = false;
+ watchpoint_sp->TurnOnEphemeralMode();
+ process_sp->DisableWatchpoint(watchpoint_sp, notify);
+ process_sp->AddPreResumeAction(SentryPreResumeAction, this);
+ }
+
+ void DoReenable() {
+ bool was_disabled = watchpoint_sp->IsDisabledDuringEphemeralMode();
+ watchpoint_sp->TurnOffEphemeralMode();
+ constexpr bool notify = false;
+ if (was_disabled) {
+ process_sp->DisableWatchpoint(watchpoint_sp, notify);
+ } else {
+ process_sp->EnableWatchpoint(watchpoint_sp, notify);
+ }
+ }
+
+ ~WatchpointSentry() {
+ DoReenable();
+ process_sp->ClearPreResumeAction(SentryPreResumeAction, this);
+ }
+
+ static bool SentryPreResumeAction(void *sentry_void) {
+ WatchpointSentry *sentry = static_cast<WatchpointSentry *>(sentry_void);
+ sentry->DoReenable();
+ return true;
+ }
+
+ private:
+ lldb::ProcessSP process_sp;
+ lldb::WatchpointSP watchpoint_sp;
+ };
+
Watchpoint(Target &target, lldb::addr_t addr, uint32_t size,
- const CompilerType *type, bool hardware = true);
+ const CompilerType *type, lldb::WatchpointMode mode);
+
+ Watchpoint(Target &target, llvm::StringRef expr, uint32_t size,
+ ExecutionContext &exe_ctx);
~Watchpoint() override;
bool IsEnabled() const;
- // This doesn't really enable/disable the watchpoint. It is currently just
- // for use in the Process plugin's {Enable,Disable}Watchpoint, which should
- // be used instead.
+ // This doesn't really enable/disable the watchpoint. It is currently
+ // just for use in the Process plugin's {Enable,Disable}Watchpoint, which
+ // should be used instead.
void SetEnabled(bool enabled, bool notify = true);
- bool IsHardware() const override;
+ bool IsHardware() const override {
+ return m_mode == lldb::eWatchpointModeHardware;
+ }
bool ShouldStop(StoppointCallbackContext *context) override;
- bool WatchpointRead() const;
- bool WatchpointWrite() const;
- bool WatchpointModify() const;
+ bool WatchpointRead() const { return m_watch_type & LLDB_WATCH_TYPE_READ; }
+ bool WatchpointWrite() const { return m_watch_type & LLDB_WATCH_TYPE_WRITE; }
+ bool WatchpointModify() const {
+ return m_watch_type & LLDB_WATCH_TYPE_MODIFY;
+ }
+
uint32_t GetIgnoreCount() const;
void SetIgnoreCount(uint32_t n);
void SetWatchpointType(uint32_t type, bool notify = true);
void SetDeclInfo(const std::string &str);
- std::string GetWatchSpec();
+ std::string GetWatchSpec() const;
void SetWatchSpec(const std::string &str);
+
+ // This function determines whether we should report a watchpoint value
+ // change. Specifically, it checks the watchpoint condition (if present),
+ // ignore count and so on.
+ //
+ // \param[in] exe_ctx This should represent the current execution context
+ // where execution stopped. It's used only for watchpoint condition
+ // evaluation.
+ //
+ // \return Returns true if we should report a watchpoint hit.
bool WatchedValueReportable(const ExecutionContext &exe_ctx);
// Snapshot management interface.
bool IsWatchVariable() const;
void SetWatchVariable(bool val);
- bool CaptureWatchedValue(const ExecutionContext &exe_ctx);
/// \struct WatchpointVariableContext
/// \brief Represents the context of a watchpoint variable.
@@ -124,7 +183,7 @@ class Watchpoint : public std::enable_shared_from_this<Watchpoint>,
void *baton, lldb_private::StoppointCallbackContext *context,
lldb::user_id_t break_id, lldb::user_id_t break_loc_id);
- void GetDescription(Stream *s, lldb::DescriptionLevel level);
+ void GetDescription(Stream *s, lldb::DescriptionLevel level) const;
void Dump(Stream *s) const override;
bool DumpSnapshots(Stream *s, const char *prefix = nullptr) const;
void DumpWithLevel(Stream *s, lldb::DescriptionLevel description_level) const;
@@ -186,26 +245,100 @@ class Watchpoint : public std::enable_shared_from_this<Watchpoint>,
bool IsDisabledDuringEphemeralMode();
- const CompilerType &GetCompilerType() { return m_type; }
+ CompilerType GetCompilerType() const;
private:
friend class Target;
friend class WatchpointList;
- friend class StopInfoWatchpoint; // This needs to call UndoHitCount()
+
+ lldb::ValueObjectSP CalculateWatchedValue() const;
+
+ void CaptureWatchedValue(lldb::ValueObjectSP new_value_sp) {
+ m_old_value_sp = m_new_value_sp;
+ m_new_value_sp = new_value_sp;
+ }
+
+ bool CheckWatchpointCondition(const ExecutionContext &exe_ctx) const;
+
+ // This class facilitates retrieving a watchpoint's watched value.
+ //
+ // It's used by both hardware and software watchpoints to access
+ // values stored in the process memory.
+ //
+ // To retrieve the value located in the memory, the value's memory address
+ // and its CompilerType are required. ExecutionContext in this case should
+ // contain information about current process, so CalculateWatchedValue
+ // function first of all create ExecutionContext from the process of m_target.
+ class AddressWatchpointCalculateStrategy {
+ public:
+ AddressWatchpointCalculateStrategy(Target &target, lldb::addr_t addr,
+ uint32_t size, const CompilerType *type)
+ : m_target{target}, m_addr{addr},
+ m_type{CreateCompilerType(target, size, type)} {}
+
+ lldb::ValueObjectSP CalculateWatchedValue() const;
+
+ CompilerType GetCompilerType() const { return m_type; };
+
+ private:
+ static CompilerType CreateCompilerType(Target &target, uint32_t size,
+ const CompilerType *type) {
+ if (type && type->IsValid())
+ return *type;
+ // If we don't have a known type, then we force it to unsigned int of the
+ // right size.
+ return DeriveCompilerType(target, size);
+ }
+
+ static CompilerType DeriveCompilerType(Target &target, uint32_t size);
+
+ Target &m_target;
+ lldb::addr_t m_addr;
+ CompilerType m_type;
+ };
+
+ // This class facilitates retrieving a watchpoint's watched value.
+ //
+ // It's used only by software watchpoints to obtain arbitral watched
+ // value, in particular not stored in the process memory.
+ //
+ // To retrieve such values, this class evaluates watchpoint's exression,
+ // therefor it is required that ExecutionContext should know about
+ // stack frame in which watched expression was specified.
+ class ExpressionWatchpointCalculateStrategy {
+ public:
+ ExpressionWatchpointCalculateStrategy(Target &target, llvm::StringRef expr,
+ ExecutionContext exe_ctx)
+ : m_target{target}, m_expr{expr}, m_exe_ctx{exe_ctx} {
+ lldbassert(
+ m_exe_ctx.GetFramePtr() &&
+ "ExecutionContext should contain information about stack frame");
+ }
+
+ lldb::ValueObjectSP CalculateWatchedValue() const;
+
+ private:
+ Target &m_target;
+ llvm::StringRef m_expr; // ref on watchpoint's m_watch_spec_str
+ ExecutionContext m_exe_ctx; // The execution context associated
+ // with watched value.
+ };
+
+ using WatchpointCalculateStrategy =
+ std::variant<AddressWatchpointCalculateStrategy,
+ ExpressionWatchpointCalculateStrategy>;
void ResetHistoricValues() {
m_old_value_sp.reset();
m_new_value_sp.reset();
}
- void UndoHitCount() { m_hit_counter.Decrement(); }
-
Target &m_target;
- bool m_enabled; // Is this watchpoint enabled
- bool m_is_hardware; // Is this a hardware watchpoint
- bool m_is_watch_variable; // True if set via 'watchpoint set variable'.
- bool m_is_ephemeral; // True if the watchpoint is in the ephemeral mode,
- // meaning that it is
+ bool m_enabled; // Is this watchpoint enabled
+ lldb::WatchpointMode m_mode; // Is this hardware or software watchpoint
+ bool m_is_watch_variable; // True if set via 'watchpoint set variable'.
+ bool m_is_ephemeral; // True if the watchpoint is in the ephemeral mode,
+ // meaning that it is
// undergoing a pair of temporary disable/enable actions to avoid recursively
// triggering further watchpoint events.
uint32_t m_disabled_count; // Keep track of the count that the watchpoint is
@@ -213,15 +346,19 @@ class Watchpoint : public std::enable_shared_from_this<Watchpoint>,
// At the end of the ephemeral mode when the watchpoint is to be enabled
// again, we check the count, if it is more than 1, it means the user-
// supplied actions actually want the watchpoint to be disabled!
- uint32_t m_watch_read : 1, // 1 if we stop when the watched data is read from
- m_watch_write : 1, // 1 if we stop when the watched data is written to
- m_watch_modify : 1; // 1 if we stop when the watched data is changed
+ uint32_t m_watch_type;
uint32_t m_ignore_count; // Number of times to ignore this watchpoint
- std::string m_decl_str; // Declaration information, if any.
- std::string m_watch_spec_str; // Spec for the watchpoint.
+ std::string m_watch_spec_str; // Spec for the watchpoint. It is optional for a
+ // hardware watchpoint, in which it is used only
+ // for dumping, but required for a software
+ // watchpoint calculation
+ WatchpointCalculateStrategy m_calculate_strategy;
+ std::string m_decl_str; // Declaration information, if any.
lldb::ValueObjectSP m_old_value_sp;
lldb::ValueObjectSP m_new_value_sp;
- CompilerType m_type;
+ lldb::ValueObjectSP
+ m_previous_hit_value_sp; // Used in software watchpoints to ensure proper
+ // ignore count behavior
Status m_error; // An error object describing errors associated with this
// watchpoint.
WatchpointOptions m_options; // Settable watchpoint options, which is a
diff --git a/lldb/include/lldb/Interpreter/OptionGroupWatchpoint.h b/lldb/include/lldb/Interpreter/OptionGroupWatchpoint.h
index 527a2612b189b..67d5f5661d828 100644
--- a/lldb/include/lldb/Interpreter/OptionGroupWatchpoint.h
+++ b/lldb/include/lldb/Interpreter/OptionGroupWatchpoint.h
@@ -44,6 +44,7 @@ class OptionGroupWatchpoint : public OptionGroup {
WatchType watch_type;
OptionValueUInt64 watch_size;
bool watch_type_specified;
+ lldb::WatchpointMode watch_mode;
lldb::LanguageType language_type;
private:
diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h
index a8892e9c43225..3fe134e664c26 100644
--- a/lldb/include/lldb/Target/Process.h
+++ b/lldb/include/lldb/Target/Process.h
@@ -2256,6 +2256,8 @@ class Process : public std::enable_shared_from_this<Process>,
return m_watchpoint_resource_list;
}
+ llvm::SmallVector<lldb::WatchpointSP> GetEnabledSoftwareWatchpoint();
+
// When ExtendedBacktraces are requested, the HistoryThreads that are created
// need an owner -- they're saved here in the Process. The threads in this
// list are not iterated over - driver programs need to request the extended
diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h
index 0d4e11b65339e..116d4c78e09f7 100644
--- a/lldb/include/lldb/Target/Target.h
+++ b/lldb/include/lldb/Target/Target.h
@@ -795,9 +795,17 @@ class Target : public std::enable_shared_from_this<Target>,
bool resolve_indirect_symbols);
// Use this to create a watchpoint:
- lldb::WatchpointSP CreateWatchpoint(lldb::addr_t addr, size_t size,
- const CompilerType *type, uint32_t kind,
- Status &error);
+ lldb::WatchpointSP CreateWatchpointByAddress(lldb::addr_t addr, size_t size,
+ const CompilerType *type,
+ uint32_t kind,
+ lldb::WatchpointMode mode,
+ Status &error);
+
+ // Can create only software watchpoints
+ lldb::WatchpointSP CreateWatchpointByExpression(llvm::StringRef expr,
+ size_t size,
+ ExecutionContext &exe_ctx,
+ uint32_t kind, Status &error);
lldb::WatchpointSP GetLastCreatedWatchpoint() {
return m_last_created_watchpoint;
diff --git a/lldb/include/lldb/Target/ThreadPlan.h b/lldb/include/lldb/Target/ThreadPlan.h
index a7bac8cc5ecf6..0aec457d03f73 100644
--- a/lldb/include/lldb/Target/ThreadPlan.h
+++ b/lldb/include/lldb/Target/ThreadPlan.h
@@ -313,6 +313,7 @@ class ThreadPlan : public std::enable_shared_from_this<ThreadPlan>,
eKindStepThrough,
eKindStepUntil,
eKindSingleThreadTimeout,
+ eKindWatchpointStepInstruction
};
virtual ~ThreadPlan();
diff --git a/lldb/include/lldb/Target/ThreadPlanStepInstruction.h b/lldb/include/lldb/Target/ThreadPlanStepInstruction.h
index 52a5a2efc0a47..508d50b996766 100644
--- a/lldb/include/lldb/Target/ThreadPlanStepInstruction.h
+++ b/lldb/include/lldb/Target/ThreadPlanStepInstruction.h
@@ -17,8 +17,10 @@ namespace lldb_private {
class ThreadPlanStepInstruction : public ThreadPlan {
public:
- ThreadPlanStepInstruction(Thread &thread, bool step_over, bool stop_others,
- Vote report_stop_vote, Vote report_run_vote);
+ ThreadPlanStepInstruction(
+ Thread &thread, bool step_over, bool stop_others, Vote report_stop_vote,
+ Vote report_run_vote,
+ ThreadPlanKind kind = ThreadPlan::eKindStepInstruction);
~ThreadPlanStepInstruction() override;
@@ -36,6 +38,8 @@ class ThreadPlanStepInstruction : public ThreadPlan {
void SetUpState();
+ lldb::addr_t GetInstructionAddr() const { return m_instruction_addr; };
+
private:
friend lldb::ThreadPlanSP Thread::QueueThreadPlanForStepSingleInstruction(
bool step_over, bool abort_other_plans, bool stop_other_threads,
diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h
index 69e8671b6e21b..370e5dbb1f3c8 100644
--- a/lldb/include/lldb/lldb-enumerations.h
+++ b/lldb/include/lldb/lldb-enumerations.h
@@ -1076,6 +1076,9 @@ enum InstructionControlFlowKind {
FLAGS_ENUM(WatchpointKind){eWatchpointKindWrite = (1u << 0),
eWatchpointKindRead = (1u << 1)};
+/// TODO: Maybe we should extend this to any stoppoint.
+enum WatchpointMode { eWatchpointModeHardware, eWatchpointModeSoftware };
+
enum GdbSignal {
eGdbSignalBadAccess = 0x91,
eGdbSignalBadInstruction = 0x92,
diff --git a/lldb/source/Breakpoint/Watchpoint.cpp b/lldb/source/Breakpoint/Watchpoint.cpp
index f1366ca538075..4ca0e655edcb4 100644
--- a/lldb/source/Breakpoint/Watchpoint.cpp
+++ b/lldb/source/Breakpoint/Watchpoint.cpp
@@ -10,6 +10,7 @@
#include "lldb/Breakpoint/StoppointCallbackContext.h"
#include "lldb/Breakpoint/WatchpointResource.h"
+#include "lldb/Core/Debugger.h"
#include "lldb/Core/Value.h"
#include "lldb/DataFormatters/DumpValueObjectOptions.h"
#include "lldb/Expression/UserExpression.h"
@@ -26,45 +27,124 @@
using namespace lldb;
using namespace lldb_private;
-Watchpoint::Watchpoint(Target &target, lldb::addr_t addr, uint32_t size,
- const CompilerType *type, bool hardware)
- : StoppointSite(0, addr, size, hardware), m_target(target),
- m_enabled(false), m_is_hardware(hardware), m_is_watch_variable(false),
- m_is_ephemeral(false), m_disabled_count(0), m_watch_read(0),
- m_watch_write(0), m_watch_modify(0), m_ignore_count(0) {
-
- if (type && type->IsValid())
- m_type = *type;
- else {
- // If we don't have a known type, then we force it to unsigned int of the
- // right size.
- auto type_system_or_err =
- target.GetScratchTypeSystemForLanguage(eLanguageTypeC);
- if (auto err = type_system_or_err.takeError()) {
- LLDB_LOG_ERROR(GetLog(LLDBLog::Watchpoints), std::move(err),
- "Failed to set type: {0}");
- } else {
- if (auto ts = *type_system_or_err) {
- if (size <= target.GetArchitecture().GetAddressByteSize()) {
- m_type =
- ts->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 8 * size);
- } else {
- CompilerType clang_uint8_type =
- ts->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 8);
- m_type = clang_uint8_type.GetArrayType(size);
- }
- } else
- LLDB_LOG_ERROR(GetLog(LLDBLog::Watchpoints), std::move(err),
- "Failed to set type: Typesystem is no longer live: {0}");
- }
+static bool IsValueObjectsEqual(lldb::ValueObjectSP lhs,
+ lldb::ValueObjectSP rhs) {
+ lldbassert(lhs);
+ lldbassert(rhs);
+
+ Status error;
+
+ DataExtractor lhs_data;
+ lhs->GetData(lhs_data, error);
+ if (error.Fail())
+ return false;
+
+ DataExtractor rhs_data;
+ rhs->GetData(rhs_data, error);
+ if (error.Fail())
+ return false;
+
+ return llvm::equal(
+ llvm::iterator_range(lhs_data.GetDataStart(), lhs_data.GetDataEnd()),
+ llvm::iterator_range(rhs_data.GetDataStart(), rhs_data.GetDataEnd()));
+}
+
+CompilerType Watchpoint::AddressWatchpointCalculateStrategy::DeriveCompilerType(
+ Target &target, uint32_t size) {
+ auto type_system_or_err =
+ target.GetScratchTypeSystemForLanguage(eLanguageTypeC);
+
+ auto err = type_system_or_err.takeError();
+ if (err) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::Watchpoints), std::move(err),
+ "Failed to set type: {0}");
+ return CompilerType();
}
- // Set the initial value of the watched variable:
- if (m_target.GetProcessSP()) {
- ExecutionContext exe_ctx;
- m_target.GetProcessSP()->CalculateExecutionContext(exe_ctx);
- CaptureWatchedValue(exe_ctx);
+ auto ts = *type_system_or_err;
+ if (!ts) {
+ LLDB_LOG_ERROR(GetLog(LLDBLog::Watchpoints), std::move(err),
+ "Failed to set type: Typesystem is no longer live: {0}");
+ return CompilerType();
+ }
+
+ if (size <= target.GetArchitecture().GetAddressByteSize())
+ return ts->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 8 * size);
+
+ CompilerType clang_uint8_type =
+ ts->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 8);
+ return clang_uint8_type.GetArrayType(size);
+}
+
+lldb::ValueObjectSP
+Watchpoint::AddressWatchpointCalculateStrategy::CalculateWatchedValue() const {
+ if (!m_type.IsValid()) {
+ // Don't know how to report new & old values, since we couldn't make a
+ // scalar type for this watchpoint. This works around an assert in
+ // ValueObjectMemory::Create.
+ // FIXME: This should not happen, but if it does in some case we care about,
+ // we can go grab the value raw and print it as unsigned.
+ return nullptr;
}
+
+ // To obtain a value that represents memory at a given address, we only need
+ // an information about process.
+ // Create here an ExecutionContext from the current process.
+ ExecutionContext exe_ctx;
+ lldb::ProcessSP process_sp = m_target.GetProcessSP();
+ if (!process_sp)
+ return nullptr;
+ process_sp->CalculateExecutionContext(exe_ctx);
+
+ ConstString g_watch_name("$__lldb__watch_value");
+ Address watch_address(m_addr);
+ auto new_value_sp = ValueObjectMemory::Create(
+ exe_ctx.GetBestExecutionContextScope(), g_watch_name.GetStringRef(),
+ watch_address, m_type);
+ new_value_sp = new_value_sp->CreateConstantValue(g_watch_name);
+ return new_value_sp;
+}
+
+lldb::ValueObjectSP
+Watchpoint::ExpressionWatchpointCalculateStrategy::CalculateWatchedValue()
+ const {
+ EvaluateExpressionOptions options;
+ options.SetUnwindOnError(true);
+ options.SetKeepInMemory(false);
+ ValueObjectSP result_value_sp;
+ ExpressionResults result_code = m_target.EvaluateExpression(
+ m_expr, m_exe_ctx.GetFramePtr(), result_value_sp, options);
+ if (result_code != eExpressionCompleted || !result_value_sp) {
+ return nullptr;
+ }
+
+ return result_value_sp;
+}
+
+Watchpoint::Watchpoint(Target &target, lldb::addr_t addr, uint32_t size,
+ const CompilerType *type, lldb::WatchpointMode mode)
+ : StoppointSite(0, addr, size,
+ mode == lldb::eWatchpointModeHardware ? true : false),
+ m_target(target), m_enabled(false), m_mode(mode),
+ m_is_watch_variable(false), m_is_ephemeral(false), m_disabled_count(0),
+ m_ignore_count(0), m_watch_spec_str{},
+ m_calculate_strategy{
+ AddressWatchpointCalculateStrategy{target, addr, size, type}} {
+ // Set the initial value of the watched variable:
+ CaptureWatchedValue(CalculateWatchedValue());
+}
+
+Watchpoint::Watchpoint(Target &target, llvm::StringRef expr, uint32_t size,
+ ExecutionContext &exe_ctx)
+ : StoppointSite(0, LLDB_INVALID_ADDRESS, size, /*hardware =*/false),
+ m_target(target), m_enabled(false), m_mode(lldb::eWatchpointModeSoftware),
+ m_is_watch_variable(false), m_is_ephemeral(false), m_disabled_count(0),
+ m_ignore_count(0), m_watch_spec_str{expr},
+ m_calculate_strategy{ExpressionWatchpointCalculateStrategy{
+ target, m_watch_spec_str, exe_ctx}} {
+ lldbassert(!HardwareRequired());
+ // Set the initial value of the watched variable:
+ CaptureWatchedValue(CalculateWatchedValue());
}
Watchpoint::~Watchpoint() = default;
@@ -184,72 +264,121 @@ void Watchpoint::ClearCallback() {
void Watchpoint::SetDeclInfo(const std::string &str) { m_decl_str = str; }
-std::string Watchpoint::GetWatchSpec() { return m_watch_spec_str; }
+std::string Watchpoint::GetWatchSpec() const { return m_watch_spec_str; }
void Watchpoint::SetWatchSpec(const std::string &str) {
m_watch_spec_str = str;
}
-bool Watchpoint::IsHardware() const {
- lldbassert(m_is_hardware || !HardwareRequired());
- return m_is_hardware;
-}
-
bool Watchpoint::IsWatchVariable() const { return m_is_watch_variable; }
void Watchpoint::SetWatchVariable(bool val) { m_is_watch_variable = val; }
-bool Watchpoint::CaptureWatchedValue(const ExecutionContext &exe_ctx) {
- ConstString g_watch_name("$__lldb__watch_value");
- m_old_value_sp = m_new_value_sp;
- Address watch_address(GetLoadAddress());
- if (!m_type.IsValid()) {
- // Don't know how to report new & old values, since we couldn't make a
- // scalar type for this watchpoint. This works around an assert in
- // ValueObjectMemory::Create.
- // FIXME: This should not happen, but if it does in some case we care about,
- // we can go grab the value raw and print it as unsigned.
- return false;
- }
- m_new_value_sp = ValueObjectMemory::Create(
- exe_ctx.GetBestExecutionContextScope(), g_watch_name.GetStringRef(),
- watch_address, m_type);
- m_new_value_sp = m_new_value_sp->CreateConstantValue(g_watch_name);
- return (m_new_value_sp && m_new_value_sp->GetError().Success());
+lldb::ValueObjectSP Watchpoint::CalculateWatchedValue() const {
+ auto caller = [](const auto &strategy) {
+ return strategy.CalculateWatchedValue();
+ };
+ auto value_obj_sp = std::visit(caller, m_calculate_strategy);
+ if (!value_obj_sp)
+ Debugger::ReportError("Watchpoint %u: Failed to calculate watched value! "
+ "The behavior of this watchpoint may be disrupted!",
+ m_id);
+ return value_obj_sp;
}
-bool Watchpoint::WatchedValueReportable(const ExecutionContext &exe_ctx) {
- if (!m_watch_modify || m_watch_read)
- return true;
- if (!m_type.IsValid())
- return true;
+CompilerType Watchpoint::GetCompilerType() const {
+ const AddressWatchpointCalculateStrategy *addr_strategy =
+ std::get_if<AddressWatchpointCalculateStrategy>(&m_calculate_strategy);
+ if (!addr_strategy)
+ return CompilerType();
+ return addr_strategy->GetCompilerType();
+}
- ConstString g_watch_name("$__lldb__watch_value");
- Address watch_address(GetLoadAddress());
- ValueObjectSP newest_valueobj_sp = ValueObjectMemory::Create(
- exe_ctx.GetBestExecutionContextScope(), g_watch_name.GetStringRef(),
- watch_address, m_type);
- newest_valueobj_sp = newest_valueobj_sp->CreateConstantValue(g_watch_name);
- Status error;
+bool Watchpoint::CheckWatchpointCondition(
+ const ExecutionContext &exe_ctx) const {
+ if (GetConditionText() == nullptr)
+ return true;
- DataExtractor new_data;
- DataExtractor old_data;
+ Log *log = GetLog(LLDBLog::Watchpoints);
- newest_valueobj_sp->GetData(new_data, error);
- if (error.Fail())
- return true;
- m_new_value_sp->GetData(old_data, error);
- if (error.Fail())
+ // We need to make sure the user sees any parse errors in their
+ // condition, so we'll hook the constructor errors up to the
+ // debugger's Async I/O.
+ EvaluateExpressionOptions expr_options;
+ expr_options.SetUnwindOnError(true);
+ expr_options.SetIgnoreBreakpoints(true);
+ ValueObjectSP result_value_sp;
+ ExpressionResults result_code = m_target.EvaluateExpression(
+ GetConditionText(), exe_ctx.GetBestExecutionContextScope(),
+ result_value_sp, expr_options);
+
+ if (!result_value_sp || result_code != eExpressionCompleted) {
+ LLDB_LOGF(log, "Error evaluating condition:\n");
+
+ StreamString strm;
+ strm << "stopped due to an error evaluating condition of "
+ "watchpoint ";
+ GetDescription(&strm, eDescriptionLevelBrief);
+ strm << ": \"" << GetConditionText() << "\"\n";
+
+ Debugger::ReportError(strm.GetString().str(),
+ exe_ctx.GetTargetRef().GetDebugger().GetID());
return true;
+ }
- if (new_data.GetByteSize() != old_data.GetByteSize() ||
- new_data.GetByteSize() == 0)
+ Scalar scalar_value;
+ if (!result_value_sp->ResolveValue(scalar_value)) {
+ LLDB_LOGF(log, "Failed to get an integer result from the expression.");
return true;
+ }
+
+ bool should_stop = scalar_value.ULongLong(1) != 0;
+
+ LLDB_LOGF(log, "Condition successfully evaluated, result is %s.\n",
+ should_stop ? "true" : "false");
- if (memcmp(new_data.GetDataStart(), old_data.GetDataStart(),
- old_data.GetByteSize()) == 0)
- return false; // Value has not changed, user requested modify watchpoint
+ return should_stop;
+}
+bool Watchpoint::WatchedValueReportable(const ExecutionContext &exe_ctx) {
+ if (!CheckWatchpointCondition(exe_ctx)) {
+ // The condition failed, which we consider "not having hit
+ // the watchpoint".
+ return false;
+ }
+
+ lldb::ValueObjectSP current_value_sp = CalculateWatchedValue();
+ if (!current_value_sp || current_value_sp->GetError().Fail())
+ return false;
+
+ if (IsHardware()) {
+ // Don't stop if the watched region value is unmodified, and
+ // this is a Modify-type watchpoint.
+ if (WatchpointModify() && !WatchpointRead() &&
+ IsValueObjectsEqual(current_value_sp, m_new_value_sp))
+ return false;
+ } else {
+ if (m_new_value_sp && IsValueObjectsEqual(current_value_sp, m_new_value_sp))
+ return false;
+
+ if (m_previous_hit_value_sp &&
+ IsValueObjectsEqual(current_value_sp, m_previous_hit_value_sp))
+ return false;
+
+ m_previous_hit_value_sp = current_value_sp;
+ }
+
+ // All checks are passed. Hit!
+ m_hit_counter.Increment();
+
+ // Even if ignore count > 0 consider it as a hit anyway, just
+ // don't stop at this watchpoint.
+ if (m_ignore_count) {
+ --m_ignore_count;
+ return false;
+ }
+
+ CaptureWatchedValue(current_value_sp);
return true;
}
@@ -257,12 +386,10 @@ bool Watchpoint::WatchedValueReportable(const ExecutionContext &exe_ctx) {
// should continue.
bool Watchpoint::ShouldStop(StoppointCallbackContext *context) {
- m_hit_counter.Increment();
-
return IsEnabled();
}
-void Watchpoint::GetDescription(Stream *s, lldb::DescriptionLevel level) {
+void Watchpoint::GetDescription(Stream *s, lldb::DescriptionLevel level) const {
DumpWithLevel(s, level);
}
@@ -276,7 +403,7 @@ bool Watchpoint::DumpSnapshots(Stream *s, const char *prefix) const {
bool printed_anything = false;
// For read watchpoints, don't display any before/after value changes.
- if (m_watch_read && !m_watch_modify && !m_watch_write)
+ if (WatchpointRead() && !WatchpointModify() && !WatchpointWrite())
return printed_anything;
s->Printf("\n");
@@ -349,11 +476,12 @@ void Watchpoint::DumpWithLevel(Stream *s,
assert(description_level >= lldb::eDescriptionLevelBrief &&
description_level <= lldb::eDescriptionLevelVerbose);
- s->Printf("Watchpoint %u: addr = 0x%8.8" PRIx64
+ s->Printf("%s Watchpoint %u: addr = 0x%8.8" PRIx64
" size = %u state = %s type = %s%s%s",
- GetID(), GetLoadAddress(), m_byte_size,
- IsEnabled() ? "enabled" : "disabled", m_watch_read ? "r" : "",
- m_watch_write ? "w" : "", m_watch_modify ? "m" : "");
+ IsHardware() ? "Hardware" : "Software", GetID(), GetLoadAddress(),
+ m_byte_size, IsEnabled() ? "enabled" : "disabled",
+ WatchpointRead() ? "r" : "", WatchpointWrite() ? "w" : "",
+ WatchpointModify() ? "m" : "");
if (description_level >= lldb::eDescriptionLevelFull) {
if (!m_decl_str.empty())
@@ -417,32 +545,35 @@ void Watchpoint::SetEnabled(bool enabled, bool notify) {
// Within StopInfo.cpp, we purposely do disable/enable watchpoint while
// performing watchpoint actions.
}
+
bool changed = enabled != m_enabled;
m_enabled = enabled;
if (notify && !m_is_ephemeral && changed)
SendWatchpointChangedEvent(enabled ? eWatchpointEventTypeEnabled
: eWatchpointEventTypeDisabled);
+
+ if (IsHardware() || !enabled)
+ return;
+
+ // If we enable a software watchpoint, we should update
+ // m_previous_hit_value_sp
+ m_previous_hit_value_sp = CalculateWatchedValue();
}
void Watchpoint::SetWatchpointType(uint32_t type, bool notify) {
- int old_watch_read = m_watch_read;
- int old_watch_write = m_watch_write;
- int old_watch_modify = m_watch_modify;
- m_watch_read = (type & LLDB_WATCH_TYPE_READ) != 0;
- m_watch_write = (type & LLDB_WATCH_TYPE_WRITE) != 0;
- m_watch_modify = (type & LLDB_WATCH_TYPE_MODIFY) != 0;
- if (notify &&
- (old_watch_read != m_watch_read || old_watch_write != m_watch_write ||
- old_watch_modify != m_watch_modify))
+ if (!IsHardware() &&
+ (type & LLDB_WATCH_TYPE_READ || type & LLDB_WATCH_TYPE_WRITE)) {
+ Debugger::ReportWarning("Software watchpoint can only be a modify type, "
+ "setting watchpoint type to modify",
+ m_target.GetDebugger().GetID());
+ type = LLDB_WATCH_TYPE_MODIFY;
+ }
+
+ if (notify && m_watch_type != type)
SendWatchpointChangedEvent(eWatchpointEventTypeTypeChanged);
+ m_watch_type = type;
}
-bool Watchpoint::WatchpointRead() const { return m_watch_read != 0; }
-
-bool Watchpoint::WatchpointWrite() const { return m_watch_write != 0; }
-
-bool Watchpoint::WatchpointModify() const { return m_watch_modify != 0; }
-
uint32_t Watchpoint::GetIgnoreCount() const { return m_ignore_count; }
void Watchpoint::SetIgnoreCount(uint32_t n) {
diff --git a/lldb/source/Commands/CommandObjectWatchpoint.cpp b/lldb/source/Commands/CommandObjectWatchpoint.cpp
index e79c3b8939fa6..ba88a2ba1ea50 100644
--- a/lldb/source/Commands/CommandObjectWatchpoint.cpp
+++ b/lldb/source/Commands/CommandObjectWatchpoint.cpp
@@ -805,7 +805,7 @@ corresponding to the byte size of the data type.");
void DoExecute(Args &command, CommandReturnObject &result) override {
Target &target = GetTarget();
- StackFrame *frame = m_exe_ctx.GetFramePtr();
+ StackFrameSP frame_sp = m_exe_ctx.GetFrameSP();
// If no argument is present, issue an error message. There's no way to
// set a watchpoint.
@@ -839,7 +839,7 @@ corresponding to the byte size of the data type.");
uint32_t expr_path_options =
StackFrame::eExpressionPathOptionCheckPtrVsMember |
StackFrame::eExpressionPathOptionsAllowDirectIVarAccess;
- valobj_sp = frame->GetValueForVariableExpressionPath(
+ valobj_sp = frame_sp->GetValueForVariableExpressionPath(
command.GetArgumentAtIndex(0), eNoDynamicValues, expr_path_options,
var_sp, error);
@@ -902,11 +902,15 @@ corresponding to the byte size of the data type.");
error.Clear();
WatchpointSP watch_sp =
- target.CreateWatchpoint(addr, size, &compiler_type, watch_type, error);
+ target.CreateWatchpointByAddress(addr, size, &compiler_type, watch_type,
+ m_option_watchpoint.watch_mode, error);
if (!watch_sp) {
result.AppendErrorWithFormat(
- "Watchpoint creation failed (addr=0x%" PRIx64 ", size=%" PRIu64
+ "%s watchpoint creation failed (addr=0x%" PRIx64 ", size=%" PRIu64
", variable expression='%s').\n",
+ m_option_watchpoint.watch_mode == lldb::eWatchpointModeHardware
+ ? "Hardware"
+ : "Software",
addr, static_cast<uint64_t>(size), command.GetArgumentAtIndex(0));
if (const char *error_message = error.AsCString(nullptr))
result.AppendError(error_message);
@@ -923,7 +927,7 @@ corresponding to the byte size of the data type.");
watch_sp->SetDeclInfo(std::string(ss.GetString()));
}
if (var_sp->GetScope() == eValueTypeVariableLocal)
- watch_sp->SetupVariableWatchpointDisabler(m_exe_ctx.GetFrameSP());
+ watch_sp->SetupVariableWatchpointDisabler(frame_sp);
}
output_stream.Printf("Watchpoint created: ");
watch_sp->GetDescription(&output_stream, lldb::eDescriptionLevelFull);
@@ -1093,22 +1097,30 @@ class CommandObjectWatchpointSetExpression : public CommandObjectRaw {
}
Status error;
- WatchpointSP watch_sp =
- target.CreateWatchpoint(addr, size, &compiler_type, watch_type, error);
- if (watch_sp) {
- watch_sp->SetWatchSpec(std::string(expr));
- Stream &output_stream = result.GetOutputStream();
- output_stream.Printf("Watchpoint created: ");
- watch_sp->GetDescription(&output_stream, lldb::eDescriptionLevelFull);
- output_stream.EOL();
- result.SetStatus(eReturnStatusSuccessFinishResult);
- } else {
- result.AppendErrorWithFormat("Watchpoint creation failed (addr=0x%" PRIx64
- ", size=%" PRIu64 ").\n",
- addr, (uint64_t)size);
- if (error.AsCString(nullptr))
- result.AppendError(error.AsCString());
+ WatchpointSP watch_sp;
+ watch_sp =
+ target.CreateWatchpointByAddress(addr, size, &compiler_type, watch_type,
+ m_option_watchpoint.watch_mode, error);
+ if (!watch_sp) {
+ result.AppendErrorWithFormat(
+ "%s watchpoint creation failed (addr=0x%" PRIx64 ", size=%" PRIu64
+ ", expression='%s').\n",
+ m_option_watchpoint.watch_mode == eWatchpointModeHardware
+ ? "Hardware"
+ : "Software",
+ addr, static_cast<uint64_t>(size), expr.data());
+ if (const char *error_message = error.AsCString(nullptr))
+ result.AppendError(error_message);
+ return;
}
+
+ watch_sp->SetWatchSpec(std::string(expr));
+
+ Stream &output_stream = result.GetOutputStream();
+ output_stream.Printf("Watchpoint created: ");
+ watch_sp->GetDescription(&output_stream, lldb::eDescriptionLevelFull);
+ output_stream.EOL();
+ result.SetStatus(eReturnStatusSuccessFinishResult);
}
private:
diff --git a/lldb/source/Interpreter/OptionGroupWatchpoint.cpp b/lldb/source/Interpreter/OptionGroupWatchpoint.cpp
index 219f550ee5562..22699898abbc1 100644
--- a/lldb/source/Interpreter/OptionGroupWatchpoint.cpp
+++ b/lldb/source/Interpreter/OptionGroupWatchpoint.cpp
@@ -43,9 +43,26 @@ static constexpr OptionDefinition g_option_table[] = {
{LLDB_OPT_SET_1, false, "watch", 'w', OptionParser::eRequiredArgument,
nullptr, OptionEnumValues(g_watch_type), 0, eArgTypeWatchType,
"Specify the type of watching to perform."},
- {LLDB_OPT_SET_1, false, "size", 's', OptionParser::eRequiredArgument,
- nullptr, {}, 0, eArgTypeByteSize,
+ {LLDB_OPT_SET_1,
+ false,
+ "size",
+ 's',
+ OptionParser::eRequiredArgument,
+ nullptr,
+ {},
+ 0,
+ eArgTypeByteSize,
"Number of bytes to use to watch a region."},
+ {LLDB_OPT_SET_1,
+ false,
+ "software",
+ 'S',
+ OptionParser::eOptionalArgument,
+ nullptr,
+ {},
+ 0,
+ eArgTypeNone,
+ "Force to create a software watchpoint"},
{LLDB_OPT_SET_2,
false,
"language",
@@ -92,6 +109,9 @@ OptionGroupWatchpoint::SetOptionValue(uint32_t option_idx,
error = Status::FromErrorStringWithFormat(
"invalid --size option value '%s'", option_arg.str().c_str());
break;
+ case 'S':
+ watch_mode = eWatchpointModeSoftware;
+ break;
default:
llvm_unreachable("Unimplemented option");
@@ -103,6 +123,7 @@ OptionGroupWatchpoint::SetOptionValue(uint32_t option_idx,
void OptionGroupWatchpoint::OptionParsingStarting(
ExecutionContext *execution_context) {
watch_type_specified = false;
+ watch_mode = eWatchpointModeHardware;
watch_type = eWatchInvalid;
watch_size.Clear();
language_type = eLanguageTypeUnknown;
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index a2c34ddfc252e..2fb18b4003d12 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -3285,6 +3285,12 @@ Status ProcessGDBRemote::EnableWatchpoint(WatchpointSP wp_sp, bool notify) {
return error;
}
+ // Handle a software watchpoint
+ if (!wp_sp->IsHardware()) {
+ wp_sp->SetEnabled(true, notify);
+ return error;
+ }
+
bool read = wp_sp->WatchpointRead();
bool write = wp_sp->WatchpointWrite() || wp_sp->WatchpointModify();
size_t size = wp_sp->GetByteSize();
@@ -3391,33 +3397,38 @@ Status ProcessGDBRemote::DisableWatchpoint(WatchpointSP wp_sp, bool notify) {
return error;
}
- if (wp_sp->IsHardware()) {
- bool disabled_all = true;
+ // Handle a software watchpoint
+ if (!wp_sp->IsHardware()) {
+ wp_sp->SetEnabled(false, notify);
+ return error;
+ }
+
+ bool disabled_all = true;
- std::vector<WatchpointResourceSP> unused_resources;
- for (const auto &wp_res_sp : m_watchpoint_resource_list.Sites()) {
- if (wp_res_sp->ConstituentsContains(wp_sp)) {
- GDBStoppointType type = GetGDBStoppointType(wp_res_sp);
- addr_t addr = wp_res_sp->GetLoadAddress();
- size_t size = wp_res_sp->GetByteSize();
- if (m_gdb_comm.SendGDBStoppointTypePacket(type, false, addr, size,
- GetInterruptTimeout())) {
- disabled_all = false;
- } else {
- wp_res_sp->RemoveConstituent(wp_sp);
- if (wp_res_sp->GetNumberOfConstituents() == 0)
- unused_resources.push_back(wp_res_sp);
- }
+ std::vector<WatchpointResourceSP> unused_resources;
+ for (const auto &wp_res_sp : m_watchpoint_resource_list.Sites()) {
+ if (wp_res_sp->ConstituentsContains(wp_sp)) {
+ GDBStoppointType type = GetGDBStoppointType(wp_res_sp);
+ addr_t addr = wp_res_sp->GetLoadAddress();
+ size_t size = wp_res_sp->GetByteSize();
+ if (m_gdb_comm.SendGDBStoppointTypePacket(type, false, addr, size,
+ GetInterruptTimeout())) {
+ disabled_all = false;
+ } else {
+ wp_res_sp->RemoveConstituent(wp_sp);
+ if (wp_res_sp->GetNumberOfConstituents() == 0)
+ unused_resources.push_back(wp_res_sp);
}
}
- for (auto &wp_res_sp : unused_resources)
- m_watchpoint_resource_list.Remove(wp_res_sp->GetID());
-
- wp_sp->SetEnabled(false, notify);
- if (!disabled_all)
- error = Status::FromErrorString(
- "Failure disabling one of the watchpoint locations");
}
+ for (auto &wp_res_sp : unused_resources)
+ m_watchpoint_resource_list.Remove(wp_res_sp->GetID());
+
+ wp_sp->SetEnabled(false, notify);
+ if (!disabled_all)
+ Status::FromErrorString(
+ "Failure disabling one of the watchpoint locations");
+
return error;
}
diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp
index bba1230c79920..0b19b74319829 100644
--- a/lldb/source/Target/Process.cpp
+++ b/lldb/source/Target/Process.cpp
@@ -17,6 +17,7 @@
#include "lldb/Breakpoint/BreakpointLocation.h"
#include "lldb/Breakpoint/StoppointCallbackContext.h"
+#include "lldb/Breakpoint/Watchpoint.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Core/Module.h"
#include "lldb/Core/ModuleSpec.h"
@@ -2507,6 +2508,15 @@ bool Process::GetWatchpointReportedAfter() {
return reported_after;
}
+llvm::SmallVector<lldb::WatchpointSP> Process::GetEnabledSoftwareWatchpoint() {
+ llvm::SmallVector<lldb::WatchpointSP, 4> watchpoints;
+ llvm::copy_if(GetTarget().GetWatchpointList().Watchpoints(),
+ std::back_inserter(watchpoints), [](lldb::WatchpointSP wp_sp) {
+ return !wp_sp->IsHardware() && wp_sp->IsEnabled();
+ });
+ return watchpoints;
+}
+
ModuleSP Process::ReadModuleFromMemory(const FileSpec &file_spec,
lldb::addr_t header_addr,
size_t size_to_read) {
@@ -4847,6 +4857,25 @@ class RestorePlanState {
bool m_is_controlling = false;
bool m_okay_to_discard = false;
};
+
+// RestoreSoftwareWatchpoints is used to disable all enabled software
+// watchpoints and reenable them on destruction
+class RestoreSoftwareWatchpoints {
+public:
+ RestoreSoftwareWatchpoints(Process &process)
+ : m_watchpoints{process.GetEnabledSoftwareWatchpoint()} {
+ for (lldb::WatchpointSP wp_sp : m_watchpoints)
+ wp_sp->SetEnabled(false, false);
+ }
+
+ ~RestoreSoftwareWatchpoints() {
+ for (lldb::WatchpointSP wp_sp : m_watchpoints)
+ wp_sp->SetEnabled(true, false);
+ }
+
+private:
+ llvm::SmallVector<lldb::WatchpointSP> m_watchpoints;
+};
} // anonymous namespace
static microseconds
@@ -4987,6 +5016,11 @@ Process::RunThreadPlan(ExecutionContext &exe_ctx,
RestorePlanState thread_plan_restorer(thread_plan_sp);
+ // We need to disable all software watchpoints, if they are presented,
+ // otherwise the process is going to step during the thread plan evaluation.
+
+ RestoreSoftwareWatchpoints sw_watchpoints_restorer(*this);
+
// We rely on the thread plan we are running returning "PlanCompleted" if
// when it successfully completes. For that to be true the plan can't be
// private - since private plans suppress themselves in the GetCompletedPlan
diff --git a/lldb/source/Target/StopInfo.cpp b/lldb/source/Target/StopInfo.cpp
index 19f89b8246926..7676ae283f950 100644
--- a/lldb/source/Target/StopInfo.cpp
+++ b/lldb/source/Target/StopInfo.cpp
@@ -28,6 +28,8 @@
#include "lldb/Utility/StreamString.h"
#include "lldb/ValueObject/ValueObject.h"
+#include "llvm/ADT/ScopeExit.h"
+
using namespace lldb;
using namespace lldb_private;
@@ -648,50 +650,6 @@ class StopInfoBreakpoint : public StopInfo {
class StopInfoWatchpoint : public StopInfo {
public:
- // Make sure watchpoint is properly disabled and subsequently enabled while
- // performing watchpoint actions.
- class WatchpointSentry {
- public:
- WatchpointSentry(ProcessSP p_sp, WatchpointSP w_sp) : process_sp(p_sp),
- watchpoint_sp(w_sp) {
- if (process_sp && watchpoint_sp) {
- const bool notify = false;
- watchpoint_sp->TurnOnEphemeralMode();
- process_sp->DisableWatchpoint(watchpoint_sp, notify);
- process_sp->AddPreResumeAction(SentryPreResumeAction, this);
- }
- }
-
- void DoReenable() {
- if (process_sp && watchpoint_sp) {
- bool was_disabled = watchpoint_sp->IsDisabledDuringEphemeralMode();
- watchpoint_sp->TurnOffEphemeralMode();
- const bool notify = false;
- if (was_disabled) {
- process_sp->DisableWatchpoint(watchpoint_sp, notify);
- } else {
- process_sp->EnableWatchpoint(watchpoint_sp, notify);
- }
- }
- }
-
- ~WatchpointSentry() {
- DoReenable();
- if (process_sp)
- process_sp->ClearPreResumeAction(SentryPreResumeAction, this);
- }
-
- static bool SentryPreResumeAction(void *sentry_void) {
- WatchpointSentry *sentry = (WatchpointSentry *) sentry_void;
- sentry->DoReenable();
- return true;
- }
-
- private:
- ProcessSP process_sp;
- WatchpointSP watchpoint_sp;
- };
-
StopInfoWatchpoint(Thread &thread, break_id_t watch_id, bool silently_skip_wp)
: StopInfo(thread, watch_id), m_silently_skip_wp(silently_skip_wp) {}
@@ -893,159 +851,106 @@ class StopInfoWatchpoint : public StopInfo {
}
void PerformAction(Event *event_ptr) override {
- Log *log = GetLog(LLDBLog::Watchpoints);
- // We're going to calculate if we should stop or not in some way during the
- // course of this code. Also by default we're going to stop, so set that
- // here.
- m_should_stop = true;
+ ThreadSP thread_sp(m_thread_wp.lock());
+ if (!thread_sp)
+ return;
+ auto Deferred = llvm::make_scope_exit([this]() {
+ Log *log = GetLog(LLDBLog::Watchpoints);
+ LLDB_LOGF(log,
+ "Process::%s returning from action with m_should_stop: %d.",
+ __FUNCTION__, m_should_stop);
+ m_should_stop_is_valid = true;
+ });
- ThreadSP thread_sp(m_thread_wp.lock());
- if (thread_sp) {
+ WatchpointSP wp_sp(
+ thread_sp->CalculateTarget()->GetWatchpointList().FindByID(GetValue()));
+ if (!wp_sp) {
+ Log *log_process(GetLog(LLDBLog::Process));
- WatchpointSP wp_sp(
- thread_sp->CalculateTarget()->GetWatchpointList().FindByID(
- GetValue()));
- if (wp_sp) {
- // This sentry object makes sure the current watchpoint is disabled
- // while performing watchpoint actions, and it is then enabled after we
- // are finished.
- ExecutionContext exe_ctx(thread_sp->GetStackFrameAtIndex(0));
- ProcessSP process_sp = exe_ctx.GetProcessSP();
+ LLDB_LOGF(log_process,
+ "Process::%s could not find watchpoint id: %" PRId64 "...",
+ __FUNCTION__, m_value);
+ m_should_stop = true;
+ return;
+ }
- WatchpointSentry sentry(process_sp, wp_sp);
+ // We're going to calculate if we should stop or not in some way during the
+ // course of this code. Also by default we're not going to stop, so set
+ // that here.
+ m_should_stop = false;
- if (m_silently_skip_wp) {
- m_should_stop = false;
- wp_sp->UndoHitCount();
- }
+ ExecutionContext exe_ctx(thread_sp->GetStackFrameAtIndex(0));
- if (wp_sp->GetHitCount() <= wp_sp->GetIgnoreCount()) {
- m_should_stop = false;
- m_should_stop_is_valid = true;
- }
+ // This sentry object makes sure the current watchpoint is disabled
+ // while performing watchpoint actions, and it is then enabled after we
+ // are finished.
+ ProcessSP process_sp = exe_ctx.GetProcessSP();
+ Watchpoint::WatchpointSentry sentry(process_sp, wp_sp);
- Debugger &debugger = exe_ctx.GetTargetRef().GetDebugger();
-
- if (m_should_stop && wp_sp->GetConditionText() != nullptr) {
- // We need to make sure the user sees any parse errors in their
- // condition, so we'll hook the constructor errors up to the
- // debugger's Async I/O.
- ExpressionResults result_code;
- EvaluateExpressionOptions expr_options;
- expr_options.SetUnwindOnError(true);
- expr_options.SetIgnoreBreakpoints(true);
- ValueObjectSP result_value_sp;
- result_code = UserExpression::Evaluate(
- exe_ctx, expr_options, wp_sp->GetConditionText(),
- llvm::StringRef(), result_value_sp);
-
- if (result_code == eExpressionCompleted) {
- if (result_value_sp) {
- Scalar scalar_value;
- if (result_value_sp->ResolveValue(scalar_value)) {
- if (scalar_value.ULongLong(1) == 0) {
- // The condition failed, which we consider "not having hit
- // the watchpoint" so undo the hit count here.
- wp_sp->UndoHitCount();
- m_should_stop = false;
- } else
- m_should_stop = true;
- LLDB_LOGF(log,
- "Condition successfully evaluated, result is %s.\n",
- m_should_stop ? "true" : "false");
- } else {
- m_should_stop = true;
- LLDB_LOGF(
- log,
- "Failed to get an integer result from the expression.");
- }
- }
- } else {
- const char *err_str = "<unknown error>";
- if (result_value_sp)
- err_str = result_value_sp->GetError().AsCString();
-
- LLDB_LOGF(log, "Error evaluating condition: \"%s\"\n", err_str);
-
- StreamString strm;
- strm << "stopped due to an error evaluating condition of "
- "watchpoint ";
- wp_sp->GetDescription(&strm, eDescriptionLevelBrief);
- strm << ": \"" << wp_sp->GetConditionText() << "\"\n";
- strm << err_str;
-
- Debugger::ReportError(strm.GetString().str(),
- exe_ctx.GetTargetRef().GetDebugger().GetID());
- }
- }
+ if (wp_sp->IsHardware()) {
+ // Hardware watchpoint may want to be skipped, so check it here.
+ if (m_silently_skip_wp)
+ return;
- // If the condition says to stop, we run the callback to further decide
- // whether to stop.
- if (m_should_stop) {
- // FIXME: For now the callbacks have to run in async mode - the
- // first time we restart we need
- // to get out of there. So set it here.
- // When we figure out how to nest watchpoint hits then this will
- // change.
+ // Watchpoint condition, ignore count and other watchpoint's staff of a
+ // hardware watchpoint haven't still been checked, so do checks here too.
+ if (!wp_sp->WatchedValueReportable(exe_ctx))
+ return;
+ }
- bool old_async = debugger.GetAsyncExecution();
- debugger.SetAsyncExecution(true);
+ // Watchpoint hit!
+ // Now run a watchpoint callback if it is preserved and report a hit.
+ if (!RunWatchpointCallback(exe_ctx, wp_sp, event_ptr))
+ return;
- StoppointCallbackContext context(event_ptr, exe_ctx, false);
- bool stop_requested = wp_sp->InvokeCallback(&context);
+ // Also make sure that the callback hasn't continued the target. If
+ // it did, when we'll set m_should_stop to false and get out of here.
+ if (HasTargetRunSinceMe())
+ return;
- debugger.SetAsyncExecution(old_async);
+ // Finally, if we are going to stop, print out the new & old values:
+ m_should_stop = true;
+ ReportWatchpointHit(exe_ctx, wp_sp);
+ }
- // Also make sure that the callback hasn't continued the target. If
- // it did, when we'll set m_should_stop to false and get out of here.
- if (HasTargetRunSinceMe())
- m_should_stop = false;
+private:
+ void SetStepOverPlanComplete() {
+ assert(m_using_step_over_plan);
+ m_step_over_plan_complete = true;
+ }
- if (m_should_stop && !stop_requested) {
- // We have been vetoed by the callback mechanism.
- m_should_stop = false;
- }
- }
+ static void ReportWatchpointHit(const ExecutionContext &exe_ctx,
+ lldb::WatchpointSP wp_sp) {
+ Debugger &debugger = exe_ctx.GetTargetRef().GetDebugger();
+ StreamSP output_sp = debugger.GetAsyncOutputStream();
+ if (wp_sp->DumpSnapshots(output_sp.get())) {
+ output_sp->EOL();
+ output_sp->Flush();
+ }
+ }
- // Don't stop if the watched region value is unmodified, and
- // this is a Modify-type watchpoint.
- if (m_should_stop && !wp_sp->WatchedValueReportable(exe_ctx)) {
- wp_sp->UndoHitCount();
- m_should_stop = false;
- }
+ static bool RunWatchpointCallback(const ExecutionContext &exe_ctx,
+ WatchpointSP wp_sp, Event *event_ptr) {
+ // FIXME: For now the callbacks have to run in async mode - the
+ // first time we restart we need
+ // to get out of there. So set it here.
+ // When we figure out how to nest watchpoint hits then this will
+ // change.
- // Finally, if we are going to stop, print out the new & old values:
- if (m_should_stop) {
- wp_sp->CaptureWatchedValue(exe_ctx);
+ Debugger &debugger = exe_ctx.GetTargetRef().GetDebugger();
- Debugger &debugger = exe_ctx.GetTargetRef().GetDebugger();
- StreamUP output_up = debugger.GetAsyncOutputStream();
- if (wp_sp->DumpSnapshots(output_up.get()))
- output_up->EOL();
- }
+ bool old_async = debugger.GetAsyncExecution();
+ debugger.SetAsyncExecution(true);
- } else {
- Log *log_process(GetLog(LLDBLog::Process));
+ StoppointCallbackContext context(event_ptr, exe_ctx, false);
+ bool stop_requested = wp_sp->InvokeCallback(&context);
- LLDB_LOGF(log_process,
- "Process::%s could not find watchpoint id: %" PRId64 "...",
- __FUNCTION__, m_value);
- }
- LLDB_LOGF(log,
- "Process::%s returning from action with m_should_stop: %d.",
- __FUNCTION__, m_should_stop);
+ debugger.SetAsyncExecution(old_async);
- m_should_stop_is_valid = true;
- }
+ return stop_requested;
}
-private:
- void SetStepOverPlanComplete() {
- assert(m_using_step_over_plan);
- m_step_over_plan_complete = true;
- }
-
bool m_should_stop = false;
bool m_should_stop_is_valid = false;
// A false watchpoint hit has happened -
diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp
index 7f569173eba20..b0329560ebadf 100644
--- a/lldb/source/Target/Target.cpp
+++ b/lldb/source/Target/Target.cpp
@@ -919,9 +919,9 @@ bool Target::ProcessIsValid() {
return (m_process_sp && m_process_sp->IsAlive());
}
-static bool CheckIfWatchpointsSupported(Target *target, Status &error) {
+static bool CheckIfHardwareWatchpointsSupported(Target &target, Status &error) {
std::optional<uint32_t> num_supported_hardware_watchpoints =
- target->GetProcessSP()->GetWatchpointSlotCount();
+ target.GetProcessSP()->GetWatchpointSlotCount();
// If unable to determine the # of watchpoints available,
// assume they are supported.
@@ -937,102 +937,216 @@ static bool CheckIfWatchpointsSupported(Target *target, Status &error) {
return true;
}
+static bool IsModifyWatchpointKind(uint32_t kind) {
+ return (kind & LLDB_WATCH_TYPE_READ) == 0 &&
+ (kind & LLDB_WATCH_TYPE_WRITE) == 0;
+}
+
+static bool CheckSoftwareWatchpointParameters(uint32_t kind, Status &error) {
+ if (!IsModifyWatchpointKind(kind)) {
+ error.FromErrorString("software watchpoint can be only \"modify\" type");
+ return false;
+ }
+ return true;
+}
+
+static bool CheckGeneralWatchpointParameters(uint32_t kind, size_t size,
+ Status &error) {
+ if (!LLDB_WATCH_TYPE_IS_VALID(kind)) {
+ error.FromErrorStringWithFormat("invalid watchpoint type: %d", kind);
+ return false;
+ }
+
+ if (size == 0) {
+ error.FromErrorString("cannot set a watchpoint with watch_size of 0");
+ return false;
+ }
+
+ return true;
+}
+
+// LWP_TODO this sequence is looking for an existing watchpoint
+// at the exact same user-specified address. If addr/size/type match,
+// reuse the matched watchpoint. If type/size/mode differ, return an error.
+// This isn't correct, we need both watchpoints to use a shared
+// WatchpointResource in the target, and expand the WatchpointResource
+// to handle the needs of both Watchpoints.
+// Also, even if the addresses don't match, they may need to be
+// supported by the same WatchpointResource, e.g. a watchpoint
+// watching 1 byte at 0x102 and a watchpoint watching 1 byte at 0x103.
+// They're in the same word and must be watched by a single hardware
+// watchpoint register.
+static WatchpointSP CheckMatchedWatchpoint(Target &target, lldb::addr_t addr,
+ uint32_t kind, size_t size,
+ WatchpointMode mode, Status &error) {
+ // Grab the list mutex while doing operations.
+ std::unique_lock<std::recursive_mutex> lock;
+ target.GetWatchpointList().GetListMutex(lock);
+
+ WatchpointList &wp_list = target.GetWatchpointList();
+ WatchpointSP matched_sp = wp_list.FindByAddress(addr);
+ if (!matched_sp)
+ return nullptr;
+
+ size_t old_size = matched_sp->GetByteSize();
+ uint32_t old_type =
+ (matched_sp->WatchpointRead() ? LLDB_WATCH_TYPE_READ : 0) |
+ (matched_sp->WatchpointWrite() ? LLDB_WATCH_TYPE_WRITE : 0) |
+ (matched_sp->WatchpointModify() ? LLDB_WATCH_TYPE_MODIFY : 0);
+ WatchpointMode old_mode = matched_sp->IsHardware() ? eWatchpointModeHardware
+ : eWatchpointModeSoftware;
+ // Return the existing watchpoint if size, type and mode match.
+ if (size == old_size && kind == old_type && mode == old_mode) {
+ Debugger::ReportWarning(llvm::formatv(
+ "Address 0x{0:x} is already monitored by Watchpoint {1} with "
+ "matching parameters: size ({2}), type ({3}{4}{5}), and mode ({6}). "
+ "Reusing existing watchpoint.",
+ addr, matched_sp->GetID(), size,
+ matched_sp->WatchpointRead() ? "r" : "",
+ matched_sp->WatchpointWrite() ? "w" : "",
+ matched_sp->WatchpointModify() ? "m" : "",
+ matched_sp->IsHardware() ? "Hardware" : "Software"));
+ WatchpointSP wp_sp = matched_sp;
+ return wp_sp;
+ }
+
+ error.FromErrorStringWithFormat(
+ "Address 0x%lx is already monitored by Watchpoint %u with "
+ "diffrent size(%zu), type(%s%s%s) or mode(%s).\n"
+ "Multiple watchpoints on the same address are not supported. "
+ "You should manually delete Watchpoint %u before setting a "
+ "a new watchpoint on this address.",
+ addr, matched_sp->GetID(), size, matched_sp->WatchpointRead() ? "r" : "",
+ matched_sp->WatchpointWrite() ? "w" : "",
+ matched_sp->WatchpointModify() ? "m" : "",
+ matched_sp->IsHardware() ? "Hardware" : "Software", matched_sp->GetID());
+ return nullptr;
+}
+
+static bool AddWatchpointToList(Target &target, WatchpointSP wp_sp,
+ Status &error) {
+ lldbassert(wp_sp);
+
+ Log *log = GetLog(LLDBLog::Watchpoints);
+ constexpr bool notify = true;
+
+ WatchpointList &wp_list = target.GetWatchpointList();
+
+ // Grab the list mutex while doing operations.
+ std::unique_lock<std::recursive_mutex> lock;
+ wp_list.GetListMutex(lock);
+
+ wp_list.Add(wp_sp, notify);
+
+ error = target.GetProcessSP()->EnableWatchpoint(wp_sp, /*notify=*/false);
+ LLDB_LOGF(log, "Target::%s (creation of watchpoint %s with id = %u)\n",
+ __FUNCTION__, error.Success() ? "succeeded" : "failed",
+ wp_sp->GetID());
+
+ if (error.Fail()) {
+ // Enabling the watchpoint on the device side failed. Remove the said
+ // watchpoint from the list maintained by the target instance.
+ wp_list.Remove(wp_sp->GetID(), notify);
+ return false;
+ }
+ return true;
+}
+
// See also Watchpoint::SetWatchpointType(uint32_t type) and the
// OptionGroupWatchpoint::WatchType enum type.
-WatchpointSP Target::CreateWatchpoint(lldb::addr_t addr, size_t size,
- const CompilerType *type, uint32_t kind,
- Status &error) {
+WatchpointSP Target::CreateWatchpointByAddress(lldb::addr_t addr, size_t size,
+ const CompilerType *type,
+ uint32_t kind,
+ WatchpointMode mode,
+ Status &error) {
Log *log = GetLog(LLDBLog::Watchpoints);
LLDB_LOGF(log,
"Target::%s (addr = 0x%8.8" PRIx64 " size = %" PRIu64
" type = %u)\n",
- __FUNCTION__, addr, (uint64_t)size, kind);
+ __FUNCTION__, addr, static_cast<uint64_t>(size), kind);
- WatchpointSP wp_sp;
if (!ProcessIsValid()) {
error = Status::FromErrorString("process is not alive");
- return wp_sp;
+ return nullptr;
}
- if (addr == LLDB_INVALID_ADDRESS || size == 0) {
- if (size == 0)
- error = Status::FromErrorString(
- "cannot set a watchpoint with watch_size of 0");
- else
- error = Status::FromErrorStringWithFormat(
- "invalid watch address: %" PRIu64, addr);
- return wp_sp;
+ if (!CheckGeneralWatchpointParameters(kind, size, error))
+ return nullptr;
+
+ if (addr == LLDB_INVALID_ADDRESS) {
+ error = Status::FromErrorStringWithFormat("invalid watch address: %" PRIu64,
+ addr);
+ return nullptr;
}
- if (!LLDB_WATCH_TYPE_IS_VALID(kind)) {
- error =
- Status::FromErrorStringWithFormat("invalid watchpoint type: %d", kind);
+ // Mask off ignored bits from watchpoint address.
+ if (ABISP abi = m_process_sp->GetABI(); abi != nullptr)
+ addr = abi->FixDataAddress(addr);
+
+ if (mode == eWatchpointModeHardware) {
+ // Hardware wathpoint specific checks
+ if (!CheckIfHardwareWatchpointsSupported(*this, error))
+ return nullptr;
+ } else {
+ // Software watchpoint specific checks
+ if (!CheckSoftwareWatchpointParameters(kind, error))
+ return nullptr;
+ }
+
+ WatchpointSP wp_sp =
+ CheckMatchedWatchpoint(*this, addr, kind, size, mode, error);
+ if (error.Fail()) {
+ // A watchpoint already exists at this address with conflicting parameters
+ // (size, type, or mode). Since multiple watchpoints cannot share the same
+ // address, return an error here and suggest the user to remove the existing
+ // watchpoint if user still wants to create a new one.
+ return nullptr;
}
- if (!CheckIfWatchpointsSupported(this, error))
+ if (wp_sp) {
+ // A watchpoint with desired parameters already exists at this address.
+ // In this case, enable it if needed and reuse it.
+ error = m_process_sp->EnableWatchpoint(wp_sp, /*notify=*/false);
return wp_sp;
+ }
- // Currently we only support one watchpoint per address, with total number of
- // watchpoints limited by the hardware which the inferior is running on.
+ wp_sp = std::make_shared<Watchpoint>(*this, addr, size, type, mode);
+ wp_sp->SetWatchpointType(kind, /*notify=*/false);
- // Grab the list mutex while doing operations.
- const bool notify = false; // Don't notify about all the state changes we do
- // on creating the watchpoint.
+ if (!AddWatchpointToList(*this, wp_sp, error))
+ return nullptr;
- // Mask off ignored bits from watchpoint address.
- if (ABISP abi = m_process_sp->GetABI())
- addr = abi->FixDataAddress(addr);
+ m_last_created_watchpoint = wp_sp;
+ return wp_sp;
+}
- // LWP_TODO this sequence is looking for an existing watchpoint
- // at the exact same user-specified address, disables the new one
- // if addr/size/type match. If type/size differ, disable old one.
- // This isn't correct, we need both watchpoints to use a shared
- // WatchpointResource in the target, and expand the WatchpointResource
- // to handle the needs of both Watchpoints.
- // Also, even if the addresses don't match, they may need to be
- // supported by the same WatchpointResource, e.g. a watchpoint
- // watching 1 byte at 0x102 and a watchpoint watching 1 byte at 0x103.
- // They're in the same word and must be watched by a single hardware
- // watchpoint register.
+WatchpointSP Target::CreateWatchpointByExpression(llvm::StringRef expr,
+ size_t size,
+ ExecutionContext &exe_ctx,
+ uint32_t kind,
+ Status &error) {
+ Log *log = GetLog(LLDBLog::Watchpoints);
+ LLDB_LOGF(log, "Target::%s (expr = %s, size = %" PRIu64 " type = %u)\n",
+ __FUNCTION__, expr.data(), static_cast<uint64_t>(size), kind);
- std::unique_lock<std::recursive_mutex> lock;
- this->GetWatchpointList().GetListMutex(lock);
- WatchpointSP matched_sp = m_watchpoint_list.FindByAddress(addr);
- if (matched_sp) {
- size_t old_size = matched_sp->GetByteSize();
- uint32_t old_type =
- (matched_sp->WatchpointRead() ? LLDB_WATCH_TYPE_READ : 0) |
- (matched_sp->WatchpointWrite() ? LLDB_WATCH_TYPE_WRITE : 0) |
- (matched_sp->WatchpointModify() ? LLDB_WATCH_TYPE_MODIFY : 0);
- // Return the existing watchpoint if both size and type match.
- if (size == old_size && kind == old_type) {
- wp_sp = matched_sp;
- wp_sp->SetEnabled(false, notify);
- } else {
- // Nil the matched watchpoint; we will be creating a new one.
- m_process_sp->DisableWatchpoint(matched_sp, notify);
- m_watchpoint_list.Remove(matched_sp->GetID(), true);
- }
+ if (!ProcessIsValid()) {
+ error.FromErrorString("process is not alive");
+ return nullptr;
}
- if (!wp_sp) {
- wp_sp = std::make_shared<Watchpoint>(*this, addr, size, type);
- wp_sp->SetWatchpointType(kind, notify);
- m_watchpoint_list.Add(wp_sp, true);
- }
+ if (!CheckGeneralWatchpointParameters(kind, size, error))
+ return nullptr;
- error = m_process_sp->EnableWatchpoint(wp_sp, notify);
- LLDB_LOGF(log, "Target::%s (creation of watchpoint %s with id = %u)\n",
- __FUNCTION__, error.Success() ? "succeeded" : "failed",
- wp_sp->GetID());
+ if (!CheckSoftwareWatchpointParameters(kind, error))
+ return nullptr;
- if (error.Fail()) {
- // Enabling the watchpoint on the device side failed. Remove the said
- // watchpoint from the list maintained by the target instance.
- m_watchpoint_list.Remove(wp_sp->GetID(), true);
- wp_sp.reset();
- } else
- m_last_created_watchpoint = wp_sp;
+ WatchpointSP wp_sp = std::make_shared<Watchpoint>(*this, expr, size, exe_ctx);
+ wp_sp->SetWatchpointType(kind, /*notify=*/false);
+
+ if (!AddWatchpointToList(*this, wp_sp, error))
+ return nullptr;
+
+ m_last_created_watchpoint = wp_sp;
return wp_sp;
}
diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp
index c68894808eacc..fd1bbf39d94eb 100644
--- a/lldb/source/Target/Thread.cpp
+++ b/lldb/source/Target/Thread.cpp
@@ -8,6 +8,7 @@
#include "lldb/Target/Thread.h"
#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Breakpoint/Watchpoint.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Core/FormatEntity.h"
#include "lldb/Core/Module.h"
@@ -620,6 +621,109 @@ void Thread::WillStop() {
current_plan->WillStop();
}
+// This plan handles two responsibilities:
+//
+// 1. Ensuring instruction stepping on resume when software watchpoints
+// are present. Since resume state is controlled by the current
+// thread plan, we must guarantee the top thread plan has
+// eStateStepping resume state. Therefore, if any software
+// watchpoints exist, this plan is manually pushed onto the thread plan
+// stack just before resume. The only exception here is
+// StepOverBreakpoint plan, which must be placed above this plan
+// when needed - otherwise we won't even have an ability to make a
+// step.
+//
+// 2. After stopping, it checks all software watchpoint values and sets
+// correspoinding stop reason when a hit occurs.
+//
+// Generally, this plan is OkeyToDiscard and not Controlling. When no
+// watchpoint hits occur after stepping, we want lower thread plans to
+// check if they are completed.
+//
+// However, when watchpoint hit is detected, this plan becomes Controlling
+// and not OkeyToDiscard. In this case, it sets the watchpoint stop reason
+// and prevents lower plans from overriding this reason.
+class ThreadPlanWatchpointStepInstruction : public ThreadPlanStepInstruction {
+public:
+ ThreadPlanWatchpointStepInstruction(Thread &thread)
+ : ThreadPlanStepInstruction(thread, /*step_over =*/false,
+ /*stop_other_threads =*/false, eVoteNoOpinion,
+ eVoteNoOpinion,
+ ThreadPlan::eKindWatchpointStepInstruction) {}
+
+ void GetDescription(Stream *s, lldb::DescriptionLevel level) override {
+ if (level == lldb::eDescriptionLevelBrief) {
+ s->Printf("watchpoint instruction step");
+ } else {
+ s->Printf("Watchpoint stepping one instruction past");
+ DumpAddress(s->AsRawOstream(), GetInstructionAddr(), sizeof(addr_t));
+ }
+
+ if (m_status.Fail())
+ s->Printf(" failed (%s)", m_status.AsCString());
+ }
+
+ bool DoPlanExplainsStop(Event *event_ptr) override {
+
+ auto watchID = GetHittedWatchID();
+ if (watchID == LLDB_INVALID_WATCH_ID)
+ return ThreadPlanStepInstruction::DoPlanExplainsStop(event_ptr);
+
+ SetPlanComplete();
+ SetStopInfo(
+ StopInfo::CreateStopReasonWithWatchpointID(GetThread(), watchID));
+ return true;
+ }
+
+ bool ShouldStop(Event *event_ptr) override {
+ if (IsPlanComplete())
+ return true;
+
+ if (!ThreadPlanStepInstruction::ShouldStop(event_ptr)) {
+ // Looks like we didn't even make a step so get out of here
+ return false;
+ }
+
+ auto watchID = GetHittedWatchID();
+ if (watchID == LLDB_INVALID_WATCH_ID)
+ return false;
+
+ SetPlanComplete();
+ SetStopInfo(
+ StopInfo::CreateStopReasonWithWatchpointID(GetThread(), watchID));
+ return true;
+ }
+
+private:
+ lldb::user_id_t GetHittedWatchID() {
+ Thread &thread = GetThread();
+ lldb::ProcessSP process_sp = thread.GetProcess();
+ if (!process_sp)
+ return LLDB_INVALID_WATCH_ID;
+
+ auto sw_watchpoints = process_sp->GetEnabledSoftwareWatchpoint();
+
+ ExecutionContext exe_ctx(thread.GetStackFrameAtIndex(0));
+ auto wp_iter = llvm::find_if(
+ sw_watchpoints, [&exe_ctx, process_sp](lldb::WatchpointSP wp_sp) {
+ Watchpoint::WatchpointSentry sentry(process_sp, wp_sp);
+ return wp_sp->WatchedValueReportable(exe_ctx);
+ });
+
+ if (wp_iter == sw_watchpoints.end())
+ return LLDB_INVALID_WATCH_ID;
+
+ auto wp_sp = *wp_iter;
+ return wp_sp->GetID();
+ }
+};
+
+static bool AlreadyHasWatchpointStepPlan(Thread &thread) {
+ auto current_plan_kind = thread.GetCurrentPlan()->GetKind();
+ return current_plan_kind == ThreadPlan::eKindStepOverBreakpoint ||
+ current_plan_kind == ThreadPlan::eKindWatchpointStepInstruction;
+}
+
bool Thread::SetupToStepOverBreakpointIfNeeded(RunDirection direction) {
if (GetResumeState() != eStateSuspended) {
// First check whether this thread is going to "actually" resume at all.
@@ -630,13 +734,33 @@ bool Thread::SetupToStepOverBreakpointIfNeeded(RunDirection direction) {
if (GetCurrentPlan()->IsVirtualStep())
return false;
+ // If we have at least one software watchpoint, push the watchpoint-step
+ // plan onto the top of the stack before the resume.
+
+ ProcessSP process_sp(GetProcess());
+ if (!process_sp)
+ return false;
+
+ // If we have at least one software watchpoint, push the watchpoint-step
+ // plan onto the top of the stack before the resume.
+ if (StateIsStoppedState(process_sp->GetState(), true) &&
+ direction == eRunForward &&
+ process_sp->GetEnabledSoftwareWatchpoint().size() >= 1 &&
+ !AlreadyHasWatchpointStepPlan(*this)) {
+ ThreadPlanSP thread_plan_sp(
+ new ThreadPlanWatchpointStepInstruction(*this));
+ // By default this plan should be OkeyToDiscard and not Controlling.
+ thread_plan_sp->SetIsControllingPlan(true);
+ thread_plan_sp->SetOkayToDiscard(false);
+ QueueThreadPlan(thread_plan_sp, false);
+ }
+
// If we're at a breakpoint push the step-over breakpoint plan. Do this
// before telling the current plan it will resume, since we might change
// what the current plan is.
lldb::RegisterContextSP reg_ctx_sp(GetRegisterContext());
- ProcessSP process_sp(GetProcess());
- if (reg_ctx_sp && process_sp && direction == eRunForward) {
+ if (reg_ctx_sp && direction == eRunForward) {
const addr_t thread_pc = reg_ctx_sp->GetPC();
BreakpointSiteSP bp_site_sp =
process_sp->GetBreakpointSiteList().FindByAddress(thread_pc);
diff --git a/lldb/source/Target/ThreadPlanStepInstruction.cpp b/lldb/source/Target/ThreadPlanStepInstruction.cpp
index 42bee79c42bbd..d3ea1c88ffb8c 100644
--- a/lldb/source/Target/ThreadPlanStepInstruction.cpp
+++ b/lldb/source/Target/ThreadPlanStepInstruction.cpp
@@ -20,13 +20,10 @@ using namespace lldb_private;
// ThreadPlanStepInstruction: Step over the current instruction
-ThreadPlanStepInstruction::ThreadPlanStepInstruction(Thread &thread,
- bool step_over,
- bool stop_other_threads,
- Vote report_stop_vote,
- Vote report_run_vote)
- : ThreadPlan(ThreadPlan::eKindStepInstruction,
- "Step over single instruction", thread, report_stop_vote,
+ThreadPlanStepInstruction::ThreadPlanStepInstruction(
+ Thread &thread, bool step_over, bool stop_other_threads,
+ Vote report_stop_vote, Vote report_run_vote, ThreadPlanKind kind)
+ : ThreadPlan(kind, "Step over single instruction", thread, report_stop_vote,
report_run_vote),
m_instruction_addr(0), m_stop_other_threads(stop_other_threads),
m_step_over(step_over) {
>From 3661a76e7e8e85d670c06aae1e1dac4bf70e9016 Mon Sep 17 00:00:00 2001
From: Daniil Avdeev <daniil.avdeev at syntacore.com>
Date: Mon, 10 Feb 2025 03:19:23 +0000
Subject: [PATCH 2/5] [lldb][python-api] add software watchpoints support in
the Python API
This patch adds support for software breakpoints in the
Python API.
SBWatchpointOptions, in addition to the watchpoint type,
also contains information on whether the breakpoint is
hardware or software.
The interfaces SBValue::Watch and SBValue::WatchPointee
have been changed. Now, instead of using bool Read and
bool Write to define the breakpoint type, these
functions take SBWatchpointOptions as an argument.
Before patch:
lldb::SBWatchpoint SBValue::Watch(bool resolve_location,
bool read, bool write,SBError &error);
After patch:
lldb::SBWatchpoint SBValue::Watch(bool resolve_location,
SBWatchpointOptions options, SBError &error);
---
lldb/include/lldb/API/SBValue.h | 6 +-
lldb/include/lldb/API/SBWatchpointOptions.h | 3 +
lldb/source/API/SBTarget.cpp | 30 ++---
lldb/source/API/SBValue.cpp | 116 ++++++++++----------
lldb/source/API/SBWatchpointOptions.cpp | 9 ++
5 files changed, 88 insertions(+), 76 deletions(-)
diff --git a/lldb/include/lldb/API/SBValue.h b/lldb/include/lldb/API/SBValue.h
index 0f788ff602b70..d2785db9a0932 100644
--- a/lldb/include/lldb/API/SBValue.h
+++ b/lldb/include/lldb/API/SBValue.h
@@ -356,7 +356,7 @@ class LLDB_API SBValue {
/// return due to a value not being contained in memory, too
/// large, or watchpoint resources are not available or all in
/// use.
- lldb::SBWatchpoint Watch(bool resolve_location, bool read, bool write,
+ lldb::SBWatchpoint Watch(bool resolve_location, SBWatchpointOptions options,
SBError &error);
// Backward compatibility fix in the interim.
@@ -386,8 +386,8 @@ class LLDB_API SBValue {
/// return due to a value not being contained in memory, too
/// large, or watchpoint resources are not available or all in
/// use.
- lldb::SBWatchpoint WatchPointee(bool resolve_location, bool read, bool write,
- SBError &error);
+ lldb::SBWatchpoint WatchPointee(bool resolve_location,
+ SBWatchpointOptions options, SBError &error);
/// If this value represents a C++ class that has a vtable, return an value
/// that represents the virtual function table.
diff --git a/lldb/include/lldb/API/SBWatchpointOptions.h b/lldb/include/lldb/API/SBWatchpointOptions.h
index 5d1d6ab9e09ff..ad682314e86c9 100644
--- a/lldb/include/lldb/API/SBWatchpointOptions.h
+++ b/lldb/include/lldb/API/SBWatchpointOptions.h
@@ -33,6 +33,9 @@ class LLDB_API SBWatchpointOptions {
void SetWatchpointTypeWrite(lldb::WatchpointWriteType write_type);
lldb::WatchpointWriteType GetWatchpointTypeWrite() const;
+ void SetWatchpointMode(lldb::WatchpointMode mode);
+ lldb::WatchpointMode GetWatchpointMode() const;
+
private:
// This auto_pointer is made in the constructor and is always valid.
mutable std::unique_ptr<WatchpointOptionsImpl> m_opaque_up;
diff --git a/lldb/source/API/SBTarget.cpp b/lldb/source/API/SBTarget.cpp
index f26f7951edc6f..df69926b15b8e 100644
--- a/lldb/source/API/SBTarget.cpp
+++ b/lldb/source/API/SBTarget.cpp
@@ -1301,8 +1301,7 @@ lldb::SBWatchpoint SBTarget::WatchAddress(lldb::addr_t addr, size_t size,
SBWatchpointOptions options;
options.SetWatchpointTypeRead(read);
- if (modify)
- options.SetWatchpointTypeWrite(eWatchpointWriteTypeOnModify);
+ options.SetWatchpointTypeWrite(eWatchpointWriteTypeOnModify);
return WatchpointCreateByAddress(addr, size, options, error);
}
@@ -1313,7 +1312,10 @@ SBTarget::WatchpointCreateByAddress(lldb::addr_t addr, size_t size,
LLDB_INSTRUMENT_VA(this, addr, size, options, error);
SBWatchpoint sb_watchpoint;
- lldb::WatchpointSP watchpoint_sp;
+ TargetSP target_sp(GetSP());
+ if (!target_sp)
+ return sb_watchpoint;
+
uint32_t watch_type = 0;
if (options.GetWatchpointTypeRead())
watch_type |= LLDB_WATCH_TYPE_READ;
@@ -1327,19 +1329,17 @@ SBTarget::WatchpointCreateByAddress(lldb::addr_t addr, size_t size,
return sb_watchpoint;
}
- if (TargetSP target_sp = GetSP();
- target_sp && addr != LLDB_INVALID_ADDRESS && size > 0) {
- std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
- // Target::CreateWatchpoint() is thread safe.
- Status cw_error;
- // This API doesn't take in a type, so we can't figure out what it is.
- CompilerType *type = nullptr;
- watchpoint_sp =
- target_sp->CreateWatchpoint(addr, size, type, watch_type, cw_error);
- error.SetError(std::move(cw_error));
- sb_watchpoint.SetSP(watchpoint_sp);
- }
+ // Target::CreateWatchpointByAddress() is thread safe.
+ std::lock_guard<std::recursive_mutex> guard(target_sp->GetAPIMutex());
+
+ Status cw_error;
+ // This API doesn't take in a type, so we can't figure out what it is.
+ CompilerType *type = nullptr;
+ lldb::WatchpointSP watchpoint_sp = target_sp->CreateWatchpointByAddress(
+ addr, size, type, watch_type, options.GetWatchpointMode(), cw_error);
+ error.SetError(std::move(cw_error));
+ sb_watchpoint.SetSP(watchpoint_sp);
return sb_watchpoint;
}
diff --git a/lldb/source/API/SBValue.cpp b/lldb/source/API/SBValue.cpp
index d878eb427a18d..3b6fdcc64990d 100644
--- a/lldb/source/API/SBValue.cpp
+++ b/lldb/source/API/SBValue.cpp
@@ -1450,69 +1450,65 @@ lldb::SBDeclaration SBValue::GetDeclaration() {
return decl_sb;
}
-lldb::SBWatchpoint SBValue::Watch(bool resolve_location, bool read, bool write,
- SBError &error) {
- LLDB_INSTRUMENT_VA(this, resolve_location, read, write, error);
+lldb::SBWatchpoint SBValue::Watch(bool resolve_location,
+ SBWatchpointOptions options, SBError &error) {
+ LLDB_INSTRUMENT_VA(this, resolve_location, options, error);
SBWatchpoint sb_watchpoint;
// If the SBValue is not valid, there's no point in even trying to watch it.
ValueLocker locker;
lldb::ValueObjectSP value_sp(GetSP(locker));
- TargetSP target_sp(GetTarget().GetSP());
- if (value_sp && target_sp) {
- // Read and Write cannot both be false.
- if (!read && !write)
- return sb_watchpoint;
-
- // If the value is not in scope, don't try and watch and invalid value
- if (!IsInScope())
- return sb_watchpoint;
-
- addr_t addr = GetLoadAddress();
- if (addr == LLDB_INVALID_ADDRESS)
- return sb_watchpoint;
- size_t byte_size = GetByteSize();
- if (byte_size == 0)
- return sb_watchpoint;
-
- uint32_t watch_type = 0;
- if (read) {
- watch_type |= LLDB_WATCH_TYPE_READ;
- // read + write, the most likely intention
- // is to catch all writes to this, not just
- // value modifications.
- if (write)
- watch_type |= LLDB_WATCH_TYPE_WRITE;
- } else {
- if (write)
- watch_type |= LLDB_WATCH_TYPE_MODIFY;
- }
-
- Status rc;
- CompilerType type(value_sp->GetCompilerType());
- WatchpointSP watchpoint_sp =
- target_sp->CreateWatchpoint(addr, byte_size, &type, watch_type, rc);
- error.SetError(std::move(rc));
-
- if (watchpoint_sp) {
- sb_watchpoint.SetSP(watchpoint_sp);
- Declaration decl;
- if (value_sp->GetDeclaration(decl)) {
- if (decl.GetFile()) {
- StreamString ss;
- // True to show fullpath for declaration file.
- decl.DumpStopContext(&ss, true);
- watchpoint_sp->SetDeclInfo(std::string(ss.GetString()));
- }
- }
- }
- } else if (target_sp) {
+ if (!value_sp) {
error = Status::FromErrorStringWithFormat("could not get SBValue: %s",
locker.GetError().AsCString());
- } else {
+ return sb_watchpoint;
+ }
+
+ TargetSP target_sp(GetTarget().GetSP());
+ if (!target_sp) {
error = Status::FromErrorString(
"could not set watchpoint, a target is required");
+ return sb_watchpoint;
+ }
+
+ // If the value is not in scope, don't try and watch and invalid value
+ if (!IsInScope())
+ return sb_watchpoint;
+
+ uint32_t watch_type = 0;
+ if (options.GetWatchpointTypeRead())
+ watch_type |= LLDB_WATCH_TYPE_READ;
+ if (options.GetWatchpointTypeWrite() == eWatchpointWriteTypeAlways)
+ watch_type |= LLDB_WATCH_TYPE_WRITE;
+ if (options.GetWatchpointTypeWrite() == eWatchpointWriteTypeOnModify)
+ watch_type |= LLDB_WATCH_TYPE_MODIFY;
+ if (!watch_type) {
+ error.SetErrorString("Can't create a watchpoint that is neither read nor "
+ "write nor modify.");
+ return sb_watchpoint;
+ }
+
+ Status rc;
+ WatchpointSP watchpoint_sp;
+ CompilerType type(value_sp->GetCompilerType());
+ watchpoint_sp = target_sp->CreateWatchpointByAddress(
+ GetLoadAddress(), GetByteSize(), &type, watch_type,
+ options.GetWatchpointMode(), rc);
+ error.SetError(std::move(rc));
+
+ if (!watchpoint_sp)
+ return sb_watchpoint;
+
+ watchpoint_sp->SetWatchSpec(GetName());
+
+ sb_watchpoint.SetSP(watchpoint_sp);
+ Declaration decl;
+ if (value_sp->GetDeclaration(decl) && decl.GetFile()) {
+ StreamString ss;
+ // True to show fullpath for declaration file.
+ decl.DumpStopContext(&ss, true);
+ watchpoint_sp->SetDeclInfo(std::string(ss.GetString()));
}
return sb_watchpoint;
@@ -1526,16 +1522,20 @@ lldb::SBWatchpoint SBValue::Watch(bool resolve_location, bool read,
LLDB_INSTRUMENT_VA(this, resolve_location, read, write);
SBError error;
- return Watch(resolve_location, read, write, error);
+ SBWatchpointOptions options;
+ options.SetWatchpointTypeRead(read);
+ options.SetWatchpointTypeWrite(eWatchpointWriteTypeOnModify);
+ return Watch(resolve_location, options, error);
}
-lldb::SBWatchpoint SBValue::WatchPointee(bool resolve_location, bool read,
- bool write, SBError &error) {
- LLDB_INSTRUMENT_VA(this, resolve_location, read, write, error);
+lldb::SBWatchpoint SBValue::WatchPointee(bool resolve_location,
+ SBWatchpointOptions options,
+ SBError &error) {
+ LLDB_INSTRUMENT_VA(this, resolve_location, options, error);
SBWatchpoint sb_watchpoint;
if (IsInScope() && GetType().IsPointerType())
- sb_watchpoint = Dereference().Watch(resolve_location, read, write, error);
+ sb_watchpoint = Dereference().Watch(resolve_location, options, error);
return sb_watchpoint;
}
diff --git a/lldb/source/API/SBWatchpointOptions.cpp b/lldb/source/API/SBWatchpointOptions.cpp
index 62e9c21a85795..895e4bd57a063 100644
--- a/lldb/source/API/SBWatchpointOptions.cpp
+++ b/lldb/source/API/SBWatchpointOptions.cpp
@@ -20,6 +20,7 @@ class WatchpointOptionsImpl {
bool m_read = false;
bool m_write = false;
bool m_modify = false;
+ lldb::WatchpointMode m_mode = lldb::eWatchpointModeHardware;
};
@@ -71,3 +72,11 @@ WatchpointWriteType SBWatchpointOptions::GetWatchpointTypeWrite() const {
return eWatchpointWriteTypeAlways;
return eWatchpointWriteTypeDisabled;
}
+
+void SBWatchpointOptions::SetWatchpointMode(lldb::WatchpointMode mode) {
+ m_opaque_up->m_mode = mode;
+}
+
+lldb::WatchpointMode SBWatchpointOptions::GetWatchpointMode() const {
+ return m_opaque_up->m_mode;
+}
>From 018724e49cdc580c0a03c105d16f8ecba9cc1e25 Mon Sep 17 00:00:00 2001
From: Daniil Avdeev <daniil.avdeev at syntacore.com>
Date: Mon, 10 Feb 2025 03:20:11 +0000
Subject: [PATCH 3/5] [lldb] add software watchpoints tests
This patch adds software watchpoints tests
---
.../lldbsuite/test/lldbwatchpointutils.py | 55 +++++++++
.../hello_watchlocation/TestWatchLocation.py | 25 +++-
.../hello_watchpoint/TestMyFirstWatchpoint.py | 21 +++-
.../TestWatchpointMultipleSlots.py | 24 +++-
.../multiple_hits/TestMultipleHits.py | 12 +-
.../TestWatchpointMultipleThreads.py | 79 ++++++++++--
.../TestStepOverWatchpoint.py | 26 +++-
.../TestUnalignedWatchpoint.py | 18 ++-
.../TestWatchedVarHitWhenInScope.py | 19 ++-
.../TestWatchTaggedAddress.py | 35 +++++-
.../TestWatchpointCommands.py | 113 +++++++++++++++---
.../command/TestWatchpointCommandLLDB.py | 37 +++++-
.../command/TestWatchpointCommandPython.py | 34 +++++-
.../condition/TestWatchpointConditionCmd.py | 14 ++-
.../watchpoints/watchpoint_commands/main.c | 10 +-
.../watchpoint_count/TestWatchpointCount.py | 16 ++-
.../TestWatchpointDisable.py | 26 +++-
.../watchpoint_events/TestWatchpointEvents.py | 15 ++-
.../TestValueOfVectorVariable.py | 22 +++-
.../TestWatchLocationWithWatchSet.py | 18 ++-
.../watchpoint_size/TestWatchpointSizes.py | 65 ++++++++--
.../large-watchpoint/TestLargeWatchpoint.py | 14 ++-
.../TestModifyWatchpoint.py | 14 ++-
.../TestUnalignedLargeWatchpoint.py | 25 +++-
.../unaligned-large-watchpoint/main.c | 2 +-
.../TestUnalignedSpanningDwords.py | 19 ++-
.../default-constructor/sb_value.py | 9 +-
.../watchpoint/TestSetWatchpoint.py | 60 ++++++++--
.../watchpoint/TestWatchpointIgnoreCount.py | 18 ++-
.../watchpoint/TestWatchpointIter.py | 14 ++-
.../condition/TestWatchpointConditionAPI.py | 12 +-
lldb/test/API/python_api/watchpoint/main.c | 10 +-
.../watchlocation/TestSetWatchlocation.py | 15 ++-
.../watchlocation/TestTargetWatchAddress.py | 112 +++++------------
34 files changed, 775 insertions(+), 233 deletions(-)
create mode 100644 lldb/packages/Python/lldbsuite/test/lldbwatchpointutils.py
diff --git a/lldb/packages/Python/lldbsuite/test/lldbwatchpointutils.py b/lldb/packages/Python/lldbsuite/test/lldbwatchpointutils.py
new file mode 100644
index 0000000000000..33483b716734d
--- /dev/null
+++ b/lldb/packages/Python/lldbsuite/test/lldbwatchpointutils.py
@@ -0,0 +1,55 @@
+import os
+import os.path
+from enum import Enum
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.gdbclientutils import *
+
+
+class WatchpointType(list[str], Enum):
+ READ = ["r", "read"]
+ WRITE = ["w", "write"]
+ READ_WRITE = ["rw", "read_write"]
+ MODIFY = ["m", "modify"]
+
+
+class WatchpointCLICommandVariant(str, Enum):
+ EXPRESSION = "expression"
+ VARIABLE = "variable"
+
+
+def _get_SBWatchpointOptions(wp_type, wp_mode):
+ wp_opts = lldb.SBWatchpointOptions()
+
+ if wp_type == WatchpointType.READ or wp_type == WatchpointType.READ_WRITE:
+ wp_opts.SetWatchpointTypeRead(True)
+ if wp_type == WatchpointType.WRITE or wp_type == WatchpointType.READ_WRITE:
+ wp_opts.SetWatchpointTypeWrite(lldb.eWatchpointWriteTypeAlways)
+ if wp_type == WatchpointType.MODIFY:
+ wp_opts.SetWatchpointTypeWrite(lldb.eWatchpointWriteTypeOnModify)
+
+ wp_opts.SetWatchpointMode(wp_mode)
+
+ return wp_opts
+
+
+def get_set_watchpoint_CLI_command(command_variant, wp_type, wp_mode):
+ cmd = ["watchpoint set", command_variant.value, "-w", wp_type.value[1]]
+ if wp_mode == lldb.eWatchpointModeSoftware:
+ cmd.append("-S")
+ return " ".join(map(str, cmd))
+
+
+def set_watchpoint_at_value(value, wp_type, wp_mode, error):
+ wp_opts = _get_SBWatchpointOptions(wp_type, wp_mode)
+ return value.Watch(True, wp_opts, error)
+
+
+def set_watchpoint_at_pointee(value, wp_type, wp_mode, error):
+ wp_opts = _get_SBWatchpointOptions(wp_type, wp_mode)
+ return value.WatchPointee(True, wp_opts, error)
+
+
+def set_watchpoint_by_address(target, addr, size, wp_type, wp_mode, error):
+ wp_opts = _get_SBWatchpointOptions(wp_type, wp_mode)
+ return target.WatchpointCreateByAddress(addr, size, wp_opts, error)
diff --git a/lldb/test/API/commands/watchpoints/hello_watchlocation/TestWatchLocation.py b/lldb/test/API/commands/watchpoints/hello_watchlocation/TestWatchLocation.py
index 8e3133864ee59..d7c2d6717e2ef 100644
--- a/lldb/test/API/commands/watchpoints/hello_watchlocation/TestWatchLocation.py
+++ b/lldb/test/API/commands/watchpoints/hello_watchlocation/TestWatchLocation.py
@@ -8,6 +8,7 @@
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
+from lldbsuite.test.lldbwatchpointutils import *
class HelloWatchLocationTestCase(TestBase):
@@ -40,9 +41,18 @@ def affected_by_radar_93863107(self):
# watchpoints are not supported yet
@expectedFailureAll(triple=re.compile("^mips"))
# SystemZ and PowerPC also currently supports only one H/W watchpoint
- @expectedFailureAll(archs=["powerpc64le", "s390x"])
+ @expectedFailureAll(archs=["powerpc64le", "s390x", "^riscv.*"])
@skipIfWindows # This test is flaky on Windows
- def test_hello_watchlocation(self):
+ def test_hello_hardware_watchlocation(self):
+ self.do_hello_watchlocation(WatchpointType.WRITE, lldb.eWatchpointModeHardware)
+
+ def test_hello_software_watchlocation(self):
+ # The software watchpoints can only be of the modify type, so in this test,
+ # we will try to use modify type watchpoints instead of the ones used in the
+ # original test (write type).
+ self.do_hello_watchlocation(WatchpointType.MODIFY, lldb.eWatchpointModeSoftware)
+
+ def do_hello_watchlocation(self, wp_type, wp_mode):
"""Test watching a location with '-s size' option."""
self.build(dictionary=self.d)
self.setTearDownCleanup(dictionary=self.d)
@@ -67,14 +77,15 @@ def test_hello_watchlocation(self):
# Now let's set a write-type watchpoint pointed to by 'g_char_ptr'.
self.expect(
- "watchpoint set expression -w write -s 1 -- g_char_ptr",
+ f"{get_set_watchpoint_CLI_command(WatchpointCLICommandVariant.EXPRESSION, wp_type, wp_mode)} -s 1 -- g_char_ptr",
WATCHPOINT_CREATED,
- substrs=["Watchpoint created", "size = 1", "type = w"],
+ substrs=["Watchpoint created", "size = 1", f"type = {wp_type.value[0]}"],
)
# Get a hold of the watchpoint id just created, it is used later on to
# match the watchpoint id which is expected to be fired.
match = re.match(
- "Watchpoint created: Watchpoint (.*):", self.res.GetOutput().splitlines()[0]
+ f"Watchpoint created: {'Hardware' if wp_mode == lldb.eWatchpointModeHardware else 'Software'} Watchpoint (.*):",
+ self.res.GetOutput().splitlines()[0],
)
if match:
expected_wp_id = int(match.group(1), 0)
@@ -84,7 +95,9 @@ def test_hello_watchlocation(self):
self.runCmd("expr unsigned val = *g_char_ptr; val")
self.expect(self.res.GetOutput().splitlines()[0], exe=False, endstr=" = 0")
- self.runCmd("watchpoint set expression -w write -s 4 -- &threads[0]")
+ self.runCmd(
+ f"{get_set_watchpoint_CLI_command(WatchpointCLICommandVariant.EXPRESSION, wp_type, wp_mode)} -s 4 -- &threads[0]"
+ )
# Use the '-v' option to do verbose listing of the watchpoint.
# The hit count should be 0 initially.
diff --git a/lldb/test/API/commands/watchpoints/hello_watchpoint/TestMyFirstWatchpoint.py b/lldb/test/API/commands/watchpoints/hello_watchpoint/TestMyFirstWatchpoint.py
index c3412f9de6215..4152279fe9b4b 100644
--- a/lldb/test/API/commands/watchpoints/hello_watchpoint/TestMyFirstWatchpoint.py
+++ b/lldb/test/API/commands/watchpoints/hello_watchpoint/TestMyFirstWatchpoint.py
@@ -7,6 +7,7 @@
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
+from lldbsuite.test.lldbwatchpointutils import *
class HelloWatchpointTestCase(TestBase):
@@ -25,7 +26,19 @@ def setUp(self):
self.d = {"C_SOURCES": self.source, "EXE": self.exe_name}
@add_test_categories(["basic_process"])
- def test_hello_watchpoint_using_watchpoint_set(self):
+ @expectedFailureAll(archs="^riscv.*")
+ def test_hello_hardware_watchpoint_using_watchpoint_set(self):
+ self.do_hello_watchpoint_using_watchpoint_set(
+ WatchpointType.WRITE, lldb.eWatchpointModeHardware
+ )
+
+ @add_test_categories(["basic_process"])
+ def test_hello_software_watchpoint_using_watchpoint_set(self):
+ self.do_hello_watchpoint_using_watchpoint_set(
+ WatchpointType.MODIFY, lldb.eWatchpointModeSoftware
+ )
+
+ def do_hello_watchpoint_using_watchpoint_set(self, wp_type, wp_mode):
"""Test a simple sequence of watchpoint creation and watchpoint hit."""
self.build(dictionary=self.d)
self.setTearDownCleanup(dictionary=self.d)
@@ -52,12 +65,12 @@ def test_hello_watchpoint_using_watchpoint_set(self):
# Now let's set a write-type watchpoint for 'global'.
# There should be only one watchpoint hit (see main.c).
self.expect(
- "watchpoint set variable -w write global",
+ f"{get_set_watchpoint_CLI_command(WatchpointCLICommandVariant.VARIABLE, wp_type, wp_mode)} global",
WATCHPOINT_CREATED,
substrs=[
"Watchpoint created",
"size = 4",
- "type = w",
+ f"type = {wp_type.value[0]}",
"%s:%d" % (self.source, self.decl),
],
)
@@ -86,5 +99,5 @@ def test_hello_watchpoint_using_watchpoint_set(self):
)
# Use the '-v' option to do verbose listing of the watchpoint.
- # The hit count should now be 1.
+ # The hitcount should now be 1.
self.expect("watchpoint list -v", substrs=["hit_count = 1"])
diff --git a/lldb/test/API/commands/watchpoints/multi_watchpoint_slots/TestWatchpointMultipleSlots.py b/lldb/test/API/commands/watchpoints/multi_watchpoint_slots/TestWatchpointMultipleSlots.py
index ef726ffec70f5..897a3da3f873d 100644
--- a/lldb/test/API/commands/watchpoints/multi_watchpoint_slots/TestWatchpointMultipleSlots.py
+++ b/lldb/test/API/commands/watchpoints/multi_watchpoint_slots/TestWatchpointMultipleSlots.py
@@ -9,6 +9,7 @@
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
+from lldbsuite.test.lldbwatchpointutils import *
class WatchpointSlotsTestCase(TestBase):
@@ -27,7 +28,17 @@ def setUp(self):
# This is a arm and aarch64 specific test case. No other architectures tested.
@skipIf(archs=no_match(["arm$", "aarch64"]))
- def test_multiple_watchpoints_on_same_word(self):
+ def test_multiple_hardware_watchpoints_on_same_word(self):
+ self.do_multiple_watchpoints_on_same_word(
+ WatchpointType.MODIFY, lldb.eWatchpointModeHardware
+ )
+
+ def test_multiple_software_watchpoints_on_same_word(self):
+ self.do_multiple_watchpoints_on_same_word(
+ WatchpointType.MODIFY, lldb.eWatchpointModeSoftware
+ )
+
+ def do_multiple_watchpoints_on_same_word(self, wp_type, wp_mode):
self.build(dictionary=self.d)
self.setTearDownCleanup(dictionary=self.d)
@@ -57,7 +68,7 @@ def test_multiple_watchpoints_on_same_word(self):
# Set a watchpoint at byteArray[0]
self.expect(
- "watchpoint set variable byteArray[0]",
+ f"{get_set_watchpoint_CLI_command(WatchpointCLICommandVariant.VARIABLE, wp_type, wp_mode)} byteArray[0]",
WATCHPOINT_CREATED,
substrs=["Watchpoint created", "size = 1"],
)
@@ -68,10 +79,11 @@ def test_multiple_watchpoints_on_same_word(self):
# debugserver on ios doesn't give an error, it creates another watchpoint,
# only expect errors on non-darwin platforms.
- if not self.platformIsDarwin():
+ # Additionally, there are no restrictions on software watchpoints in this case.
+ if wp_mode == lldb.eWatchpointModeHardware and (not self.platformIsDarwin()):
# Try setting a watchpoint at byteArray[1]
self.expect(
- "watchpoint set variable byteArray[1]",
+ f"{get_set_watchpoint_CLI_command(WatchpointCLICommandVariant.VARIABLE, wp_type, wp_mode)} byteArray[1]",
error=True,
substrs=["Watchpoint creation failed"],
)
@@ -91,7 +103,7 @@ def test_multiple_watchpoints_on_same_word(self):
# Set a watchpoint at byteArray[3]
self.expect(
- "watchpoint set variable byteArray[3]",
+ f"{get_set_watchpoint_CLI_command(WatchpointCLICommandVariant.VARIABLE, wp_type, wp_mode)} byteArray[3]",
WATCHPOINT_CREATED,
substrs=["Watchpoint created", "size = 1"],
)
@@ -101,7 +113,7 @@ def test_multiple_watchpoints_on_same_word(self):
# We should be stopped due to the watchpoint.
# The stop reason of the thread should be watchpoint.
- if self.platformIsDarwin():
+ if wp_mode == lldb.eWatchpointModeSoftware or self.platformIsDarwin():
# On darwin we'll hit byteArray[3] which is watchpoint 2
self.expect(
"thread list",
diff --git a/lldb/test/API/commands/watchpoints/multiple_hits/TestMultipleHits.py b/lldb/test/API/commands/watchpoints/multiple_hits/TestMultipleHits.py
index f9c3f33190852..5766498fbb947 100644
--- a/lldb/test/API/commands/watchpoints/multiple_hits/TestMultipleHits.py
+++ b/lldb/test/API/commands/watchpoints/multiple_hits/TestMultipleHits.py
@@ -7,6 +7,7 @@
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
+from lldbsuite.test.lldbwatchpointutils import *
class MultipleHitsTestCase(TestBase):
@@ -17,8 +18,15 @@ class MultipleHitsTestCase(TestBase):
oslist=["linux"],
archs=["arm$", "aarch64", "powerpc64le"],
)
+ @expectedFailureAll(archs="^riscv.*")
@skipIfwatchOS
- def test(self):
+ def test_hw_watchpoint(self):
+ self.do_test(WatchpointType.READ_WRITE, lldb.eWatchpointModeHardware)
+
+ def test_sw_watchpoint(self):
+ self.do_test(WatchpointType.MODIFY, lldb.eWatchpointModeSoftware)
+
+ def do_test(self, wp_type, wp_mode):
self.build()
target = self.createTestTarget()
@@ -42,7 +50,7 @@ def test(self):
self.assertTrue(member and member.IsValid(), "member is valid")
error = lldb.SBError()
- watch = member.Watch(True, True, True, error)
+ watch = set_watchpoint_at_value(member, wp_type, wp_mode, error)
self.assertSuccess(error)
process.Continue()
diff --git a/lldb/test/API/commands/watchpoints/multiple_threads/TestWatchpointMultipleThreads.py b/lldb/test/API/commands/watchpoints/multiple_threads/TestWatchpointMultipleThreads.py
index fe1d4b57c8ee5..97efeca6de094 100644
--- a/lldb/test/API/commands/watchpoints/multiple_threads/TestWatchpointMultipleThreads.py
+++ b/lldb/test/API/commands/watchpoints/multiple_threads/TestWatchpointMultipleThreads.py
@@ -7,6 +7,7 @@
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
+from lldbsuite.test.lldbwatchpointutils import *
class WatchpointForMultipleThreadsTestCase(TestBase):
@@ -14,28 +15,71 @@ class WatchpointForMultipleThreadsTestCase(TestBase):
main_spec = lldb.SBFileSpec("main.cpp", False)
@skipIfWindows # This test is flaky on Windows
- def test_watchpoint_before_thread_start(self):
+ @expectedFailureAll(archs="^riscv.*")
+ def test_hardware_watchpoint_before_thread_start(self):
"""Test that we can hit a watchpoint we set before starting another thread"""
- self.do_watchpoint_test("Before running the thread")
+ self.do_watchpoint_test(
+ "Before running the thread",
+ WatchpointType.WRITE,
+ lldb.eWatchpointModeHardware,
+ )
@skipIfWindows # This test is flaky on Windows
- def test_watchpoint_after_thread_launch(self):
+ @expectedFailureAll(archs="^riscv.*")
+ def test_hardware_watchpoint_after_thread_launch(self):
"""Test that we can hit a watchpoint we set after launching another thread"""
- self.do_watchpoint_test("After launching the thread")
+ self.do_watchpoint_test(
+ "After launching the thread",
+ WatchpointType.WRITE,
+ lldb.eWatchpointModeHardware,
+ )
- def test_watchpoint_after_thread_start(self):
+ @expectedFailureAll(archs="^riscv.*")
+ def test_hardware_watchpoint_after_thread_start(self):
"""Test that we can hit a watchpoint we set after another thread starts"""
- self.do_watchpoint_test("After running the thread")
+ self.do_watchpoint_test(
+ "After running the thread",
+ WatchpointType.WRITE,
+ lldb.eWatchpointModeHardware,
+ )
- def do_watchpoint_test(self, line):
+ # The software watchpoints can only be of the modify type, so in this tests,
+ # we will try to use modify type watchpoints instead of the ones used in the
+ # original test (write type).
+
+ def test_software_watchpoint_before_thread_start(self):
+ """Test that we can hit a watchpoint we set before starting another thread"""
+ self.do_watchpoint_test(
+ "Before running the thread",
+ WatchpointType.MODIFY,
+ lldb.eWatchpointModeSoftware,
+ )
+
+ def test_software_watchpoint_after_thread_launch(self):
+ """Test that we can hit a watchpoint we set after launching another thread"""
+ self.do_watchpoint_test(
+ "After launching the thread",
+ WatchpointType.MODIFY,
+ lldb.eWatchpointModeSoftware,
+ )
+
+ def test_software_watchpoint_after_thread_start(self):
+ """Test that we can hit a watchpoint we set after another thread starts"""
+ self.do_watchpoint_test(
+ "After running the thread",
+ WatchpointType.MODIFY,
+ lldb.eWatchpointModeSoftware,
+ )
+
+ def do_watchpoint_test(self, line, wp_type, wp_mode):
self.build()
lldbutil.run_to_source_breakpoint(self, line, self.main_spec)
# Now let's set a write-type watchpoint for variable 'g_val'.
self.expect(
- "watchpoint set variable -w write g_val",
+ f"{get_set_watchpoint_CLI_command(WatchpointCLICommandVariant.VARIABLE, wp_type, wp_mode)} g_val",
WATCHPOINT_CREATED,
- substrs=["Watchpoint created", "size = 4", "type = w"],
+ substrs=["Watchpoint created", "size = 4", f"type = {wp_type.value[0]}"],
)
# Use the '-v' option to do verbose listing of the watchpoint.
@@ -55,7 +99,18 @@ def do_watchpoint_test(self, line):
# The hit count should now be 1.
self.expect("watchpoint list -v", substrs=["hit_count = 1"])
- def test_watchpoint_multiple_threads_wp_set_and_then_delete(self):
+ @expectedFailureAll(archs="^riscv.*")
+ def test_harwdare_watchpoint_multiple_threads_wp_set_and_then_delete(self):
+ self.do_watchpoint_multiple_threads_wp_set_and_then_delete(
+ WatchpointType.WRITE, lldb.eWatchpointModeHardware
+ )
+
+ def test_software_watchpoint_multiple_threads_wp_set_and_then_delete(self):
+ self.do_watchpoint_multiple_threads_wp_set_and_then_delete(
+ WatchpointType.MODIFY, lldb.eWatchpointModeSoftware
+ )
+
+ def do_watchpoint_multiple_threads_wp_set_and_then_delete(self, wp_type, wp_mode):
"""Test that lldb watchpoint works for multiple threads, and after the watchpoint is deleted, the watchpoint event should no longer fires."""
self.build()
self.setTearDownCleanup()
@@ -66,9 +121,9 @@ def test_watchpoint_multiple_threads_wp_set_and_then_delete(self):
# Now let's set a write-type watchpoint for variable 'g_val'.
self.expect(
- "watchpoint set variable -w write g_val",
+ f"{get_set_watchpoint_CLI_command(WatchpointCLICommandVariant.VARIABLE, wp_type, wp_mode)} g_val",
WATCHPOINT_CREATED,
- substrs=["Watchpoint created", "size = 4", "type = w"],
+ substrs=["Watchpoint created", "size = 4", f"type = {wp_type.value[0]}"],
)
# Use the '-v' option to do verbose listing of the watchpoint.
diff --git a/lldb/test/API/commands/watchpoints/step_over_watchpoint/TestStepOverWatchpoint.py b/lldb/test/API/commands/watchpoints/step_over_watchpoint/TestStepOverWatchpoint.py
index 8179d5288ce8e..4af556443e54d 100644
--- a/lldb/test/API/commands/watchpoints/step_over_watchpoint/TestStepOverWatchpoint.py
+++ b/lldb/test/API/commands/watchpoints/step_over_watchpoint/TestStepOverWatchpoint.py
@@ -5,7 +5,7 @@
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
-
+from lldbsuite.test.lldbwatchpointutils import *
class TestStepOverWatchpoint(TestBase):
NO_DEBUG_INFO_TESTCASE = True
@@ -19,8 +19,8 @@ def get_to_start(self, bkpt_text):
return (target, process, thread, frame, read_watchpoint)
@add_test_categories(["basic_process"])
- # kernel disables wp's over instruction step, fixed in macOS 14.4.
@skipIf(macos_version=["<", "14.4"])
+ @expectedFailureAll(archs="^riscv.*")
def test_step_over_read_watchpoint(self):
self.build()
target, process, thread, bkpt = lldbutil.run_to_source_breakpoint(
@@ -36,7 +36,9 @@ def test_step_over_read_watchpoint(self):
error = lldb.SBError()
# resolve_location=True, read=True, write=False
- read_watchpoint = read_value.Watch(True, True, False, error)
+ read_watchpoint = set_watchpoint_at_value(
+ read_value, WatchpointType.READ, lldb.eWatchpointModeHardware, error
+ )
self.assertSuccess(error, "Error while setting watchpoint")
self.assertTrue(read_watchpoint, "Failed to set read watchpoint.")
@@ -61,7 +63,21 @@ def test_step_over_read_watchpoint(self):
@add_test_categories(["basic_process"])
# kernel disables wp's over instruction step, fixed in macOS 14.4.
@skipIf(macos_version=["<", "14.4"])
- def test_step_over_write_watchpoint(self):
+ @expectedFailureAll(archs="^riscv.*")
+ def test_step_over_write_hw_watchpoint(self):
+ self.do_step_over_write_watchpoint(
+ WatchpointType.WRITE, lldb.eWatchpointModeHardware
+ )
+
+ def test_step_over_write_sw_watchpoint(self):
+ # The software watchpoints can only be of the modify type, so in this test,
+ # we will try to use modify type watchpoints instead of the ones used in the
+ # original test (write type).
+ self.do_step_over_write_watchpoint(
+ WatchpointType.MODIFY, lldb.eWatchpointModeSoftware
+ )
+
+ def do_step_over_write_watchpoint(self, wp_type, wp_mode):
self.build()
target, process, thread, bkpt = lldbutil.run_to_source_breakpoint(
self, "break here for modify watchpoints", lldb.SBFileSpec("main.c")
@@ -79,7 +95,7 @@ def test_step_over_write_watchpoint(self):
error = lldb.SBError()
# resolve_location=True, read=False, modify=True
- write_watchpoint = write_value.Watch(True, False, True, error)
+ write_watchpoint = set_watchpoint_at_value(write_value, wp_type, wp_mode, error)
self.assertTrue(write_watchpoint, "Failed to set write watchpoint.")
self.assertSuccess(error, "Error while setting watchpoint")
diff --git a/lldb/test/API/commands/watchpoints/unaligned-watchpoint/TestUnalignedWatchpoint.py b/lldb/test/API/commands/watchpoints/unaligned-watchpoint/TestUnalignedWatchpoint.py
index 5f9e52855da16..101286e9b2474 100644
--- a/lldb/test/API/commands/watchpoints/unaligned-watchpoint/TestUnalignedWatchpoint.py
+++ b/lldb/test/API/commands/watchpoints/unaligned-watchpoint/TestUnalignedWatchpoint.py
@@ -11,12 +11,24 @@
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
+from lldbsuite.test.lldbwatchpointutils import *
class UnalignedWatchpointTestCase(TestBase):
NO_DEBUG_INFO_TESTCASE = True
- def test_unaligned_watchpoint(self):
+ @expectedFailureAll(archs="^riscv.*")
+ def test_unaligned_hardware_watchpoint(self):
+ self.do_unaligned_watchpoint(
+ WatchpointType.MODIFY, lldb.eWatchpointModeHardware
+ )
+
+ def test_unaligned_software_watchpoint(self):
+ self.do_unaligned_watchpoint(
+ WatchpointType.MODIFY, lldb.eWatchpointModeSoftware
+ )
+
+ def do_unaligned_watchpoint(self, wp_type, wp_mode):
"""Test an unaligned watchpoint triggered by a larger aligned write."""
self.build()
self.main_source_file = lldb.SBFileSpec("main.c")
@@ -26,7 +38,9 @@ def test_unaligned_watchpoint(self):
frame = thread.GetFrameAtIndex(0)
- self.expect("watchpoint set variable a.buf[2]")
+ self.expect(
+ f"{get_set_watchpoint_CLI_command(WatchpointCLICommandVariant.VARIABLE, wp_type, wp_mode)} a.buf[2]"
+ )
self.runCmd("process continue")
diff --git a/lldb/test/API/commands/watchpoints/variable_out_of_scope/TestWatchedVarHitWhenInScope.py b/lldb/test/API/commands/watchpoints/variable_out_of_scope/TestWatchedVarHitWhenInScope.py
index 2f4ed5a983ecc..128e6f7a090bc 100644
--- a/lldb/test/API/commands/watchpoints/variable_out_of_scope/TestWatchedVarHitWhenInScope.py
+++ b/lldb/test/API/commands/watchpoints/variable_out_of_scope/TestWatchedVarHitWhenInScope.py
@@ -7,6 +7,7 @@
from lldbsuite.test.lldbtest import *
import lldbsuite.test.lldbutil as lldbutil
from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbwatchpointutils import *
class WatchedVariableHitWhenInScopeTestCase(TestBase):
@@ -29,7 +30,19 @@ def setUp(self):
# Test hangs due to a kernel bug, see fdfeff0f in the linux kernel for details
@skipIfTargetAndroid(api_levels=list(range(25 + 1)), archs=["aarch64", "arm$"])
@skip
- def test_watched_var_should_only_hit_when_in_scope(self):
+ @expectedFailureAll(archs="^riscv.*")
+ def test_hardware_watched_var_should_only_hit_when_in_scope(self):
+ self.do_watched_var_should_only_hit_when_in_scope(
+ WatchpointType.WRITE, lldb.eWatchpointModeHardware
+ )
+
+ @skip
+ def test_software_watched_var_should_only_hit_when_in_scope(self):
+ self.do_watched_var_should_only_hit_when_in_scope(
+ WatchpointType.MODIFY, lldb.eWatchpointModeSoftware
+ )
+
+ def do_watched_var_should_only_hit_when_in_scope(self, wp_type, wp_mode):
"""Test that a variable watchpoint should only hit when in scope."""
self.build(dictionary=self.d)
self.setTearDownCleanup(dictionary=self.d)
@@ -54,9 +67,9 @@ def test_watched_var_should_only_hit_when_in_scope(self):
# Now let's set a watchpoint for 'c.a'.
# There should be only one watchpoint hit (see main.c).
self.expect(
- "watchpoint set variable c.a",
+ f"{get_set_watchpoint_CLI_command(WatchpointCLICommandVariant.VARIABLE, wp_type, wp_mode)} c.a",
WATCHPOINT_CREATED,
- substrs=["Watchpoint created", "size = 4", "type = w"],
+ substrs=["Watchpoint created", "size = 4", f"type = {wp_type.value[0]}"],
)
# Use the '-v' option to do verbose listing of the watchpoint.
diff --git a/lldb/test/API/commands/watchpoints/watch_tagged_addr/TestWatchTaggedAddress.py b/lldb/test/API/commands/watchpoints/watch_tagged_addr/TestWatchTaggedAddress.py
index 9d9c04912d7df..2d7315f5529dd 100644
--- a/lldb/test/API/commands/watchpoints/watch_tagged_addr/TestWatchTaggedAddress.py
+++ b/lldb/test/API/commands/watchpoints/watch_tagged_addr/TestWatchTaggedAddress.py
@@ -6,6 +6,7 @@
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
+from lldbsuite.test.lldbwatchpointutils import *
class TestWatchTaggedAddresses(TestBase):
@@ -33,7 +34,18 @@ def setUp(self):
@skipIf(archs=no_match(["aarch64"]))
@skipIf(oslist=no_match(["linux"]))
- def test_watch_hit_tagged_ptr_access(self):
+ def test_hw_watch_hit_tagged_ptr_access(self):
+ self.do_watch_hit_tagged_ptr_access(
+ WatchpointType.MODIFY, lldb.eWatchpointModeHardware
+ )
+
+ @skipIf(archs=no_match(["aarch64"]))
+ def test_sw_watch_hit_tagged_ptr_access(self):
+ self.do_watch_hit_tagged_ptr_access(
+ WatchpointType.MODIFY, lldb.eWatchpointModeSoftware
+ )
+
+ def do_watch_hit_tagged_ptr_access(self, wp_type, wp_mode):
"""
Test that LLDB hits watchpoint installed on an untagged address with
memory access by a tagged pointer.
@@ -57,12 +69,12 @@ def test_watch_hit_tagged_ptr_access(self):
# Now let's set a watchpoint on 'global_var'.
self.expect(
- "watchpoint set variable global_var",
+ f"{get_set_watchpoint_CLI_command(WatchpointCLICommandVariant.VARIABLE, wp_type, wp_mode)} global_var",
WATCHPOINT_CREATED,
substrs=[
"Watchpoint created",
"size = 4",
- "type = m",
+ f"type = {wp_type.value[0]}",
"%s:%d" % (self.source, self.decl),
],
)
@@ -71,7 +83,18 @@ def test_watch_hit_tagged_ptr_access(self):
@skipIf(archs=no_match(["aarch64"]))
@skipIf(oslist=no_match(["linux"]))
- def test_watch_set_on_tagged_ptr(self):
+ def test_hw_watch_hit_tagged_ptr_access(self):
+ self.do_watch_set_on_tagged_ptr(
+ WatchpointType.MODIFY, lldb.eWatchpointModeHardware
+ )
+
+ @skipIf(archs=no_match(["aarch64"]))
+ def test_sw_watch_hit_tagged_ptr_access(self):
+ self.do_watch_set_on_tagged_ptr(
+ WatchpointType.MODIFY, lldb.eWatchpointModeSoftware
+ )
+
+ def do_watch_set_on_tagged_ptr(self, wp_type, wp_mode):
"""Test that LLDB can install and hit watchpoint on a tagged address"""
# Find the line number to break inside main().
@@ -94,9 +117,9 @@ def test_watch_set_on_tagged_ptr(self):
# Now let's set a expression watchpoint on 'tagged_ptr'.
self.expect(
- "watchpoint set expression -s 4 -- tagged_ptr",
+ f"{get_set_watchpoint_CLI_command(WatchpointCLICommandVariant.EXPRESSION, wp_type, wp_mode)} -s 4 -- tagged_ptr",
WATCHPOINT_CREATED,
- substrs=["Watchpoint created", "size = 4", "type = m"],
+ substrs=["Watchpoint created", "size = 4", f"type = {wp_type.value[0]}"],
)
self.verify_watch_hits()
diff --git a/lldb/test/API/commands/watchpoints/watchpoint_commands/TestWatchpointCommands.py b/lldb/test/API/commands/watchpoints/watchpoint_commands/TestWatchpointCommands.py
index d40d14bbe6d66..94f66f38be2f1 100644
--- a/lldb/test/API/commands/watchpoints/watchpoint_commands/TestWatchpointCommands.py
+++ b/lldb/test/API/commands/watchpoints/watchpoint_commands/TestWatchpointCommands.py
@@ -7,6 +7,7 @@
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
+from lldbsuite.test.lldbwatchpointutils import *
class WatchpointCommandsTestCase(TestBase):
@@ -31,7 +32,19 @@ def setUp(self):
# Read-write watchpoints not supported on SystemZ
@expectedFailureAll(archs=["s390x"])
- def test_rw_watchpoint(self):
+ @expectedFailureAll(archs="^riscv.*")
+ def test_rw_hardware_watchpoint(self):
+ self.do_rw_watchpoint(WatchpointType.READ_WRITE, lldb.eWatchpointModeHardware)
+
+ def test_rw_software_watchpoint(self):
+ # The software watchpoints can only be of the modify type, so in this test,
+ # we will try to use modify type watchpoints instead of the ones used in the
+ # original test (read-write type).
+ self.runCmd("settings append target.env-vars SW_WP_CASE=YES")
+ self.do_rw_watchpoint(WatchpointType.MODIFY, lldb.eWatchpointModeSoftware)
+ self.runCmd("settings clear target.env-vars")
+
+ def do_rw_watchpoint(self, wp_type, wp_mode):
"""Test read_write watchpoint and expect to stop two times."""
self.build(dictionary=self.d)
self.setTearDownCleanup(dictionary=self.d)
@@ -58,12 +71,12 @@ def test_rw_watchpoint(self):
# Now let's set a read_write-type watchpoint for 'global'.
# There should be two watchpoint hits (see main.c).
self.expect(
- "watchpoint set variable -w read_write global",
+ f"{get_set_watchpoint_CLI_command(WatchpointCLICommandVariant.VARIABLE, wp_type, wp_mode)} global",
WATCHPOINT_CREATED,
substrs=[
"Watchpoint created",
"size = 4",
- "type = rw",
+ f"type = {wp_type.value[0]}",
"%s:%d" % (self.source, self.decl),
],
)
@@ -107,7 +120,23 @@ def test_rw_watchpoint(self):
# Read-write watchpoints not supported on SystemZ
@expectedFailureAll(archs=["s390x"])
- def test_rw_watchpoint_delete(self):
+ @expectedFailureAll(archs="^riscv.*")
+ def test_rw_hardware_watchpoint_delete(self):
+ self.do_rw_watchpoint_delete(
+ WatchpointType.READ_WRITE, lldb.eWatchpointModeHardware
+ )
+
+ def test_rw_software_watchpoint_delete(self):
+ # The software watchpoints can only be of the modify type, so in this test,
+ # we will try to use modify type watchpoints instead of the ones used in the
+ # original test (read-write type).
+ self.runCmd("settings append target.env-vars SW_WP_CASE=YES")
+ self.do_rw_watchpoint_delete(
+ WatchpointType.MODIFY, lldb.eWatchpointModeSoftware
+ )
+ self.runCmd("settings clear target.env-vars")
+
+ def do_rw_watchpoint_delete(self, wp_type, wp_mode):
"""Test delete watchpoint and expect not to stop for watchpoint."""
self.build()
lldbutil.run_to_line_breakpoint(self, lldb.SBFileSpec(self.source), self.line)
@@ -115,12 +144,12 @@ def test_rw_watchpoint_delete(self):
# Now let's set a read_write-type watchpoint for 'global'.
# There should be two watchpoint hits (see main.c).
self.expect(
- "watchpoint set variable -w read_write global",
+ f"{get_set_watchpoint_CLI_command(WatchpointCLICommandVariant.VARIABLE, wp_type, wp_mode)} global",
WATCHPOINT_CREATED,
substrs=[
"Watchpoint created",
"size = 4",
- "type = rw",
+ f"type = {wp_type.value[0]}",
"%s:%d" % (self.source, self.decl),
],
)
@@ -138,12 +167,12 @@ def test_rw_watchpoint_delete(self):
# Now let's set a read_write-type watchpoint for 'global'.
# There should be two watchpoint hits (see main.c).
self.expect(
- "watchpoint set variable -w read_write global",
+ f"{get_set_watchpoint_CLI_command(WatchpointCLICommandVariant.VARIABLE, wp_type, wp_mode)} global",
WATCHPOINT_CREATED,
substrs=[
"Watchpoint created",
"size = 4",
- "type = rw",
+ f"type = {wp_type.value[0]}",
"%s:%d" % (self.source, self.decl),
],
)
@@ -161,7 +190,23 @@ def test_rw_watchpoint_delete(self):
# Read-write watchpoints not supported on SystemZ
@expectedFailureAll(archs=["s390x"])
- def test_rw_watchpoint_set_ignore_count(self):
+ @expectedFailureAll(archs="^riscv.*")
+ def test_rw_hardware_watchpoint_set_ignore_count(self):
+ self.do_rw_watchpoint_set_ignore_count(
+ WatchpointType.READ_WRITE, lldb.eWatchpointModeHardware
+ )
+
+ def test_rw_software_watchpoint_set_ignore_count(self):
+ # The software watchpoints can only be of the modify type, so in this test,
+ # we will try to use modify type watchpoints instead of the ones used in the
+ # original test (read-write type).
+ self.runCmd("settings append target.env-vars SW_WP_CASE=YES")
+ self.do_rw_watchpoint_set_ignore_count(
+ WatchpointType.MODIFY, lldb.eWatchpointModeSoftware
+ )
+ self.runCmd("settings clear target.env-vars")
+
+ def do_rw_watchpoint_set_ignore_count(self, wp_type, wp_mode):
"""Test watchpoint ignore count and expect to not to stop at all."""
self.build(dictionary=self.d)
self.setTearDownCleanup(dictionary=self.d)
@@ -188,12 +233,12 @@ def test_rw_watchpoint_set_ignore_count(self):
# Now let's set a read_write-type watchpoint for 'global'.
# There should be two watchpoint hits (see main.c).
self.expect(
- "watchpoint set variable -w read_write global",
+ f"{get_set_watchpoint_CLI_command(WatchpointCLICommandVariant.VARIABLE, wp_type, wp_mode)} global",
WATCHPOINT_CREATED,
substrs=[
"Watchpoint created",
"size = 4",
- "type = rw",
+ f"type = {wp_type.value[0]}",
"%s:%d" % (self.source, self.decl),
],
)
@@ -213,11 +258,27 @@ def test_rw_watchpoint_set_ignore_count(self):
# Use the '-v' option to do verbose listing of the watchpoint.
# Expect to find a hit_count of 2 as well.
- self.expect("watchpoint list -v", substrs=["hit_count = 2", "ignore_count = 2"])
+ self.expect("watchpoint list -v", substrs=["hit_count = 2", "ignore_count = 0"])
# Read-write watchpoints not supported on SystemZ
@expectedFailureAll(archs=["s390x"])
- def test_rw_disable_after_first_stop(self):
+ @expectedFailureAll(archs="^riscv.*")
+ def test_rw_hardware_watchpoint_disable_after_first_stop(self):
+ self.do_rw_disable_after_first_stop(
+ WatchpointType.READ_WRITE, lldb.eWatchpointModeHardware
+ )
+
+ def test_rw_software_watchpoint_disable_after_first_stop(self):
+ # The software watchpoints can only be of the modify type, so in this test,
+ # we will try to use modify type watchpoints instead of the ones used in the
+ # original test (read-write type).
+ self.runCmd("settings append target.env-vars SW_WP_CASE=YES")
+ self.do_rw_disable_after_first_stop(
+ WatchpointType.MODIFY, lldb.eWatchpointModeSoftware
+ )
+ self.runCmd("settings clear target.env-vars")
+
+ def do_rw_disable_after_first_stop(self, wp_type, wp_mode):
"""Test read_write watchpoint but disable it after the first stop."""
self.build(dictionary=self.d)
self.setTearDownCleanup(dictionary=self.d)
@@ -244,12 +305,12 @@ def test_rw_disable_after_first_stop(self):
# Now let's set a read_write-type watchpoint for 'global'.
# There should be two watchpoint hits (see main.c).
self.expect(
- "watchpoint set variable -w read_write global",
+ f"{get_set_watchpoint_CLI_command(WatchpointCLICommandVariant.VARIABLE, wp_type, wp_mode)} global",
WATCHPOINT_CREATED,
substrs=[
"Watchpoint created",
"size = 4",
- "type = rw",
+ f"type = {wp_type.value[0]}",
"%s:%d" % (self.source, self.decl),
],
)
@@ -286,7 +347,23 @@ def test_rw_disable_after_first_stop(self):
# Read-write watchpoints not supported on SystemZ
@expectedFailureAll(archs=["s390x"])
- def test_rw_disable_then_enable(self):
+ @expectedFailureAll(archs="^riscv.*")
+ def test_rw_hardware_watchpoint_disable_then_enable(self):
+ self.do_rw_disable_then_enable(
+ WatchpointType.READ_WRITE, lldb.eWatchpointModeHardware
+ )
+
+ def test_rw_software_watchpoint_disable_then_enable(self):
+ # The software watchpoints can only be of the modify type, so in this test,
+ # we will try to use modify type watchpoints instead of the ones used in the
+ # original test (read-write type).
+ self.runCmd("settings append target.env-vars SW_WP_CASE=YES")
+ self.do_rw_disable_then_enable(
+ WatchpointType.MODIFY, lldb.eWatchpointModeSoftware
+ )
+ self.runCmd("settings clear target.env-vars")
+
+ def do_rw_disable_then_enable(self, wp_type, wp_mode):
"""Test read_write watchpoint, disable initially, then enable it."""
self.build(dictionary=self.d)
self.setTearDownCleanup(dictionary=self.d)
@@ -316,12 +393,12 @@ def test_rw_disable_then_enable(self):
# Now let's set a read_write-type watchpoint for 'global'.
# There should be two watchpoint hits (see main.c).
self.expect(
- "watchpoint set variable -w read_write global",
+ f"{get_set_watchpoint_CLI_command(WatchpointCLICommandVariant.VARIABLE, wp_type, wp_mode)} global",
WATCHPOINT_CREATED,
substrs=[
"Watchpoint created",
"size = 4",
- "type = rw",
+ f"type = {wp_type.value[0]}",
"%s:%d" % (self.source, self.decl),
],
)
diff --git a/lldb/test/API/commands/watchpoints/watchpoint_commands/command/TestWatchpointCommandLLDB.py b/lldb/test/API/commands/watchpoints/watchpoint_commands/command/TestWatchpointCommandLLDB.py
index cc1fd8594a1f2..6e7f8a68afda8 100644
--- a/lldb/test/API/commands/watchpoints/watchpoint_commands/command/TestWatchpointCommandLLDB.py
+++ b/lldb/test/API/commands/watchpoints/watchpoint_commands/command/TestWatchpointCommandLLDB.py
@@ -7,6 +7,7 @@
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
+from lldbsuite.test.lldbwatchpointutils import *
class WatchpointLLDBCommandTestCase(TestBase):
@@ -26,7 +27,17 @@ def setUp(self):
self.exe_name = "a%d.out" % self.test_number
self.d = {"CXX_SOURCES": self.source, "EXE": self.exe_name}
- def test_watchpoint_command(self):
+ @expectedFailureAll(archs="^riscv.*")
+ def test_hardware_watchpoint_command(self):
+ self.do_watchpoint_command(WatchpointType.WRITE, lldb.eWatchpointModeHardware)
+
+ def test_software_watchpoint_command(self):
+ # The software watchpoints can only be of the modify type, so in this test,
+ # we will try to use modify type watchpoints instead of the ones used in the
+ # original test (write type).
+ self.do_watchpoint_command(WatchpointType.MODIFY, lldb.eWatchpointModeSoftware)
+
+ def do_watchpoint_command(self, wp_type, wp_mode):
"""Test 'watchpoint command'."""
self.build(dictionary=self.d)
self.setTearDownCleanup(dictionary=self.d)
@@ -52,12 +63,12 @@ def test_watchpoint_command(self):
# Now let's set a write-type watchpoint for 'global'.
self.expect(
- "watchpoint set variable -w write global",
+ f"{get_set_watchpoint_CLI_command(WatchpointCLICommandVariant.VARIABLE, wp_type, wp_mode)} global",
WATCHPOINT_CREATED,
substrs=[
"Watchpoint created",
"size = 4",
- "type = w",
+ f"type = {wp_type.value[0]}",
"%s:%d" % (self.source, self.decl),
],
)
@@ -99,7 +110,21 @@ def test_watchpoint_command(self):
substrs=["(int32_t)", "cookie = 777"],
)
- def test_watchpoint_command_can_disable_a_watchpoint(self):
+ @expectedFailureAll(archs="^riscv.*")
+ def test_hardware_command_can_disable_a_watchpoint(self):
+ self.do_watchpoint_command_can_disable_a_watchpoint(
+ WatchpointType.WRITE, lldb.eWatchpointModeHardware
+ )
+
+ def test_software_command_can_disable_a_watchpoint(self):
+ # The software watchpoints can only be of the modify type, so in this test,
+ # we will try to use modify type watchpoints instead of the ones used in the
+ # original test (write type).
+ self.do_watchpoint_command_can_disable_a_watchpoint(
+ WatchpointType.MODIFY, lldb.eWatchpointModeSoftware
+ )
+
+ def do_watchpoint_command_can_disable_a_watchpoint(self, wp_type, wp_mode):
"""Test that 'watchpoint command' action can disable a watchpoint after it is triggered."""
self.build(dictionary=self.d)
self.setTearDownCleanup(dictionary=self.d)
@@ -125,12 +150,12 @@ def test_watchpoint_command_can_disable_a_watchpoint(self):
# Now let's set a write-type watchpoint for 'global'.
self.expect(
- "watchpoint set variable -w write global",
+ f"{get_set_watchpoint_CLI_command(WatchpointCLICommandVariant.VARIABLE, wp_type, wp_mode)} global",
WATCHPOINT_CREATED,
substrs=[
"Watchpoint created",
"size = 4",
- "type = w",
+ f"type = {wp_type.value[0]}",
"%s:%d" % (self.source, self.decl),
],
)
diff --git a/lldb/test/API/commands/watchpoints/watchpoint_commands/command/TestWatchpointCommandPython.py b/lldb/test/API/commands/watchpoints/watchpoint_commands/command/TestWatchpointCommandPython.py
index 57dd214ba6398..cf8da7f92228e 100644
--- a/lldb/test/API/commands/watchpoints/watchpoint_commands/command/TestWatchpointCommandPython.py
+++ b/lldb/test/API/commands/watchpoints/watchpoint_commands/command/TestWatchpointCommandPython.py
@@ -8,6 +8,7 @@
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
+from lldbsuite.test.lldbwatchpointutils import *
class WatchpointPythonCommandTestCase(TestBase):
@@ -28,7 +29,17 @@ def setUp(self):
self.exe_name = self.testMethodName
self.d = {"CXX_SOURCES": self.source, "EXE": self.exe_name}
- def test_watchpoint_command(self):
+ @expectedFailureAll(archs="^riscv.*")
+ def test_hardware_watchpoint_command(self):
+ self.do_watchpoint_command(WatchpointType.WRITE, lldb.eWatchpointModeHardware)
+
+ def test_software_watchpoint_command(self):
+ # The software watchpoints can only be of the modify type, so in this test,
+ # we will try to use modify type watchpoints instead of the ones used in the
+ # original test (write type).
+ self.do_watchpoint_command(WatchpointType.MODIFY, lldb.eWatchpointModeSoftware)
+
+ def do_watchpoint_command(self, wp_type, wp_mode):
"""Test 'watchpoint command'."""
self.build(dictionary=self.d)
self.setTearDownCleanup(dictionary=self.d)
@@ -54,12 +65,12 @@ def test_watchpoint_command(self):
# Now let's set a write-type watchpoint for 'global'.
self.expect(
- "watchpoint set variable -w write global",
+ f"{get_set_watchpoint_CLI_command(WatchpointCLICommandVariant.VARIABLE, wp_type, wp_mode)} global",
WATCHPOINT_CREATED,
substrs=[
"Watchpoint created",
"size = 4",
- "type = w",
+ f"type = {wp_type.value[0]}",
"%s:%d" % (self.source, self.decl),
],
)
@@ -98,7 +109,18 @@ def test_watchpoint_command(self):
substrs=["(int32_t)", "cookie = 777"],
)
- def test_continue_in_watchpoint_command(self):
+ @expectedFailureAll(archs="^riscv.*")
+ def test_continue_in_hardware_watchpoint_command(self):
+ self.do_continue_in_watchpoint_command(
+ WatchpointType.WRITE, lldb.eWatchpointModeHardware
+ )
+
+ def test_continue_in_software_watchpoint_command(self):
+ self.do_continue_in_watchpoint_command(
+ WatchpointType.MODIFY, lldb.eWatchpointModeSoftware
+ )
+
+ def do_continue_in_watchpoint_command(self, wp_type, wp_mode):
"""Test continue in a watchpoint command."""
self.build(dictionary=self.d)
self.setTearDownCleanup(dictionary=self.d)
@@ -124,12 +146,12 @@ def test_continue_in_watchpoint_command(self):
# Now let's set a write-type watchpoint for 'global'.
self.expect(
- "watchpoint set variable -w write global",
+ f"{get_set_watchpoint_CLI_command(WatchpointCLICommandVariant.VARIABLE, wp_type, wp_mode)} global",
WATCHPOINT_CREATED,
substrs=[
"Watchpoint created",
"size = 4",
- "type = w",
+ f"type = {wp_type.value[0]}",
"%s:%d" % (self.source, self.decl),
],
)
diff --git a/lldb/test/API/commands/watchpoints/watchpoint_commands/condition/TestWatchpointConditionCmd.py b/lldb/test/API/commands/watchpoints/watchpoint_commands/condition/TestWatchpointConditionCmd.py
index 3a60859b9ce7d..28d4980bbe60a 100644
--- a/lldb/test/API/commands/watchpoints/watchpoint_commands/condition/TestWatchpointConditionCmd.py
+++ b/lldb/test/API/commands/watchpoints/watchpoint_commands/condition/TestWatchpointConditionCmd.py
@@ -7,6 +7,7 @@
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
+from lldbsuite.test.lldbwatchpointutils import *
class WatchpointConditionCmdTestCase(TestBase):
@@ -26,7 +27,14 @@ def setUp(self):
self.exe_name = self.testMethodName
self.d = {"CXX_SOURCES": self.source, "EXE": self.exe_name}
- def test_watchpoint_cond(self):
+ @expectedFailureAll(archs="^riscv.*")
+ def test_hardware_watchpoint_cond(self):
+ self.do_watchpoint_cond(WatchpointType.WRITE, lldb.eWatchpointModeHardware)
+
+ def test_software_watchpoint_cond(self):
+ self.do_watchpoint_cond(WatchpointType.MODIFY, lldb.eWatchpointModeSoftware)
+
+ def do_watchpoint_cond(self, wp_type, wp_mode):
"""Test watchpoint condition."""
self.build(dictionary=self.d)
self.setTearDownCleanup(dictionary=self.d)
@@ -53,12 +61,12 @@ def test_watchpoint_cond(self):
# Now let's set a write-type watchpoint for 'global'.
# With a condition of 'global==5'.
self.expect(
- "watchpoint set variable -w write global",
+ f"{get_set_watchpoint_CLI_command(WatchpointCLICommandVariant.VARIABLE, wp_type, wp_mode)} global",
WATCHPOINT_CREATED,
substrs=[
"Watchpoint created",
"size = 4",
- "type = w",
+ f"type = {wp_type.value[0]}",
"%s:%d" % (self.source, self.decl),
],
)
diff --git a/lldb/test/API/commands/watchpoints/watchpoint_commands/main.c b/lldb/test/API/commands/watchpoints/watchpoint_commands/main.c
index 6a3036d3ce979..5f012cad233d7 100644
--- a/lldb/test/API/commands/watchpoints/watchpoint_commands/main.c
+++ b/lldb/test/API/commands/watchpoints/watchpoint_commands/main.c
@@ -1,5 +1,6 @@
-#include <stdio.h>
#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
int32_t global = 10; // Watchpoint variable declaration.
@@ -12,5 +13,10 @@ int main(int argc, char** argv) {
local += argc;
++local; // Set 2nd break point for disable_then_enable test case.
printf("local: %d\n", local);
- printf("global=%d\n", global);
+
+ const char *s = getenv("SW_WP_CASE");
+ if (s == NULL)
+ printf("global=%d\n", global);
+ else
+ global = 30;
}
diff --git a/lldb/test/API/commands/watchpoints/watchpoint_count/TestWatchpointCount.py b/lldb/test/API/commands/watchpoints/watchpoint_count/TestWatchpointCount.py
index ff834b508d9ad..9f333828dc0d3 100644
--- a/lldb/test/API/commands/watchpoints/watchpoint_count/TestWatchpointCount.py
+++ b/lldb/test/API/commands/watchpoints/watchpoint_count/TestWatchpointCount.py
@@ -1,7 +1,10 @@
+import time
+
import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
+from lldbsuite.test.lldbwatchpointutils import *
class TestWatchpointCount(TestBase):
@@ -12,7 +15,14 @@ class TestWatchpointCount(TestBase):
archs=["arm$", "aarch64"],
bugnumber="llvm.org/pr26031",
)
- def test_watchpoint_count(self):
+ @expectedFailureAll(archs="^riscv.*")
+ def test_hw_watchpoint_count(self):
+ self.do_watchpoint_count(WatchpointType.MODIFY, lldb.eWatchpointModeHardware)
+
+ def test_sw_watchpoint_count(self):
+ self.do_watchpoint_count(WatchpointType.MODIFY, lldb.eWatchpointModeSoftware)
+
+ def do_watchpoint_count(self, wp_type, wp_mode):
self.build()
(_, process, thread, _) = lldbutil.run_to_source_breakpoint(
self, "patatino", lldb.SBFileSpec("main.c")
@@ -22,11 +32,11 @@ def test_watchpoint_count(self):
second_var = frame.FindVariable("x2")
error = lldb.SBError()
- first_watch = first_var.Watch(True, False, True, error)
+ first_watch = set_watchpoint_at_value(first_var, wp_type, wp_mode, error)
if not error.Success():
self.fail("Failed to make watchpoint for x1: %s" % (error.GetCString()))
- second_watch = second_var.Watch(True, False, True, error)
+ second_watch = set_watchpoint_at_value(second_var, wp_type, wp_mode, error)
if not error.Success():
self.fail("Failed to make watchpoint for x2: %s" % (error.GetCString()))
process.Continue()
diff --git a/lldb/test/API/commands/watchpoints/watchpoint_disable/TestWatchpointDisable.py b/lldb/test/API/commands/watchpoints/watchpoint_disable/TestWatchpointDisable.py
index 5fe5d17ca21ca..98f79650599a5 100644
--- a/lldb/test/API/commands/watchpoints/watchpoint_disable/TestWatchpointDisable.py
+++ b/lldb/test/API/commands/watchpoints/watchpoint_disable/TestWatchpointDisable.py
@@ -6,22 +6,35 @@
from lldbsuite.test.lldbtest import *
from lldbsuite.test.decorators import *
from lldbsuite.test import lldbplatform, lldbplatformutil
+from lldbsuite.test.lldbwatchpointutils import *
class TestWatchpointSetEnable(TestBase):
NO_DEBUG_INFO_TESTCASE = True
- def test_disable_works(self):
+ @expectedFailureAll(archs="^riscv.*")
+ def test_harwdare_watchpoint_disable_works(self):
"""Set a watchpoint, disable it, and make sure it doesn't get hit."""
self.build()
- self.do_test(False)
+ self.do_test(False, WatchpointType.WRITE, lldb.eWatchpointModeHardware)
- def test_disable_enable_works(self):
+ def test_software_watchpoint_disable_works(self):
"""Set a watchpoint, disable it, and make sure it doesn't get hit."""
self.build()
- self.do_test(True)
+ self.do_test(False, WatchpointType.MODIFY, lldb.eWatchpointModeSoftware)
- def do_test(self, test_enable):
+ @expectedFailureAll(archs="^riscv.*")
+ def test_harwdare_watchpoint_disable_enable_works(self):
+ """Set a watchpoint, disable it, and make sure it doesn't get hit."""
+ self.build()
+ self.do_test(True, WatchpointType.WRITE, lldb.eWatchpointModeHardware)
+
+ def test_software_watchpoint_disable_enable_works(self):
+ """Set a watchpoint, disable it, and make sure it doesn't get hit."""
+ self.build()
+ self.do_test(True, WatchpointType.MODIFY, lldb.eWatchpointModeSoftware)
+
+ def do_test(self, test_enable, wp_type, wp_mode):
"""Set a watchpoint, disable it and make sure it doesn't get hit."""
main_file_spec = lldb.SBFileSpec("main.c")
@@ -52,7 +65,8 @@ def do_test(self, test_enable):
ret_val = lldb.SBCommandReturnObject()
self.dbg.GetCommandInterpreter().HandleCommand(
- "watchpoint set variable -w write global_var", ret_val
+ f"{get_set_watchpoint_CLI_command(WatchpointCLICommandVariant.VARIABLE, wp_type, wp_mode)} global_var",
+ ret_val,
)
self.assertTrue(
ret_val.Succeeded(), "Watchpoint set variable did not return success."
diff --git a/lldb/test/API/commands/watchpoints/watchpoint_events/TestWatchpointEvents.py b/lldb/test/API/commands/watchpoints/watchpoint_events/TestWatchpointEvents.py
index 6e05cf06204a7..8e275377b250d 100644
--- a/lldb/test/API/commands/watchpoints/watchpoint_events/TestWatchpointEvents.py
+++ b/lldb/test/API/commands/watchpoints/watchpoint_events/TestWatchpointEvents.py
@@ -4,6 +4,7 @@
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
+from lldbsuite.test.lldbwatchpointutils import *
class TestWatchpointEvents(TestBase):
@@ -16,7 +17,15 @@ def setUp(self):
self.main_source = "main.c"
@add_test_categories(["pyapi"])
- def test_with_python_api(self):
+ @expectedFailureAll(archs="^riscv.*")
+ def test_hw_watchpoint_with_python_api(self):
+ self.do_with_python_api(WatchpointType.MODIFY, lldb.eWatchpointModeHardware)
+
+ @add_test_categories(["pyapi"])
+ def test_sw_watchpoint_with_python_api(self):
+ self.do_with_python_api(WatchpointType.MODIFY, lldb.eWatchpointModeSoftware)
+
+ def do_with_python_api(self, wp_type, wp_mode):
"""Test that adding, deleting and modifying watchpoints sends the appropriate events."""
self.build()
target = self.createTestTarget()
@@ -54,7 +63,7 @@ def test_with_python_api(self):
)
error = lldb.SBError()
- local_watch = local_var.Watch(True, False, True, error)
+ local_watch = set_watchpoint_at_value(local_var, wp_type, wp_mode, error)
if not error.Success():
self.fail(
"Failed to make watchpoint for local_var: %s" % (error.GetCString())
@@ -88,7 +97,7 @@ def test_with_python_api(self):
)
# Re-create it so that we can check DeleteAllWatchpoints
- local_watch = local_var.Watch(True, False, True, error)
+ local_watch = set_watchpoint_at_value(local_var, wp_type, wp_mode, error)
if not error.Success():
self.fail(
"Failed to make watchpoint for local_var: %s" % (error.GetCString())
diff --git a/lldb/test/API/commands/watchpoints/watchpoint_on_vectors/TestValueOfVectorVariable.py b/lldb/test/API/commands/watchpoints/watchpoint_on_vectors/TestValueOfVectorVariable.py
index 0a2b1fa5bbba0..c1327b079bb97 100644
--- a/lldb/test/API/commands/watchpoints/watchpoint_on_vectors/TestValueOfVectorVariable.py
+++ b/lldb/test/API/commands/watchpoints/watchpoint_on_vectors/TestValueOfVectorVariable.py
@@ -7,18 +7,32 @@
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
+from lldbsuite.test.lldbwatchpointutils import *
class TestValueOfVectorVariableTestCase(TestBase):
NO_DEBUG_INFO_TESTCASE = True
- def test_value_of_vector_variable_using_watchpoint_set(self):
+ @expectedFailureAll(archs="^riscv.*")
+ def test_value_of_vector_variable_using_hardware_watchpoint_set(self):
"""Test verify displayed value of vector variable."""
exe = self.getBuildArtifact("a.out")
d = {"C_SOURCES": self.source, "EXE": exe}
self.build(dictionary=d)
self.setTearDownCleanup(dictionary=d)
- self.value_of_vector_variable_with_watchpoint_set()
+ self.value_of_vector_variable_with_watchpoint_set(
+ WatchpointType.MODIFY, lldb.eWatchpointModeHardware
+ )
+
+ def test_value_of_vector_variable_using_software_watchpoint_set(self):
+ """Test verify displayed value of vector variable."""
+ exe = self.getBuildArtifact("a.out")
+ d = {"C_SOURCES": self.source, "EXE": exe}
+ self.build(dictionary=d)
+ self.setTearDownCleanup(dictionary=d)
+ self.value_of_vector_variable_with_watchpoint_set(
+ WatchpointType.MODIFY, lldb.eWatchpointModeSoftware
+ )
def setUp(self):
# Call super's setUp().
@@ -26,7 +40,7 @@ def setUp(self):
# Our simple source filename.
self.source = "main.c"
- def value_of_vector_variable_with_watchpoint_set(self):
+ def value_of_vector_variable_with_watchpoint_set(self, wp_type, wp_mode):
"""Test verify displayed value of vector variable"""
exe = self.getBuildArtifact("a.out")
self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
@@ -39,7 +53,7 @@ def value_of_vector_variable_with_watchpoint_set(self):
# Value of a vector variable should be displayed correctly
self.expect(
- "watchpoint set variable global_vector",
+ f"{get_set_watchpoint_CLI_command(WatchpointCLICommandVariant.VARIABLE, wp_type, wp_mode)} global_vector",
WATCHPOINT_CREATED,
substrs=["new value: (1, 2, 3, 4)"],
)
diff --git a/lldb/test/API/commands/watchpoints/watchpoint_set_command/TestWatchLocationWithWatchSet.py b/lldb/test/API/commands/watchpoints/watchpoint_set_command/TestWatchLocationWithWatchSet.py
index cd13690655e45..6e382e50f8b17 100644
--- a/lldb/test/API/commands/watchpoints/watchpoint_set_command/TestWatchLocationWithWatchSet.py
+++ b/lldb/test/API/commands/watchpoints/watchpoint_set_command/TestWatchLocationWithWatchSet.py
@@ -7,6 +7,7 @@
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
+from lldbsuite.test.lldbwatchpointutils import *
class WatchLocationUsingWatchpointSetTestCase(TestBase):
@@ -34,8 +35,19 @@ def setUp(self):
# method.
@skipIf(oslist=["linux"], archs=["aarch64", "arm$"], bugnumber="llvm.org/pr26031")
+ @expectedFailureAll(archs="^riscv.*")
@skipIfWindows # This test is flaky on Windows
- def test_watchlocation_using_watchpoint_set(self):
+ def test_watchlocation_using_hw_watchpoint_set(self):
+ self.do_watchlocation_using_watchpoint_set(
+ WatchpointType.WRITE, lldb.eWatchpointModeHardware
+ )
+
+ def test_watchlocation_using_sw_watchpoint_set(self):
+ self.do_watchlocation_using_watchpoint_set(
+ WatchpointType.MODIFY, lldb.eWatchpointModeSoftware
+ )
+
+ def do_watchlocation_using_watchpoint_set(self, wp_type, wp_mode):
"""Test watching a location with 'watchpoint set expression -w write -s size' option."""
self.build()
self.setTearDownCleanup()
@@ -64,9 +76,9 @@ def test_watchlocation_using_watchpoint_set(self):
# The main.cpp, by design, misbehaves by not following the agreed upon
# protocol of only accessing the allowable index range of [0, 6].
self.expect(
- "watchpoint set expression -w write -s 1 -- g_char_ptr + 7",
+ f"{get_set_watchpoint_CLI_command(WatchpointCLICommandVariant.EXPRESSION, wp_type, wp_mode)} -s 1 -- g_char_ptr + 7",
WATCHPOINT_CREATED,
- substrs=["Watchpoint created", "size = 1", "type = w"],
+ substrs=["Watchpoint created", "size = 1", f"type = {wp_type.value[0]}"],
)
self.runCmd("expr unsigned val = g_char_ptr[7]; val")
self.expect(self.res.GetOutput().splitlines()[0], exe=False, endstr=" = 0")
diff --git a/lldb/test/API/commands/watchpoints/watchpoint_size/TestWatchpointSizes.py b/lldb/test/API/commands/watchpoints/watchpoint_size/TestWatchpointSizes.py
index d34a96266119f..f9cdf0989fa8f 100644
--- a/lldb/test/API/commands/watchpoints/watchpoint_size/TestWatchpointSizes.py
+++ b/lldb/test/API/commands/watchpoints/watchpoint_size/TestWatchpointSizes.py
@@ -10,6 +10,7 @@
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
+from lldbsuite.test.lldbwatchpointutils import *
class WatchpointSizeTestCase(TestBase):
@@ -28,23 +29,65 @@ def setUp(self):
# Read-write watchpoints not supported on SystemZ
@expectedFailureAll(archs=["s390x"])
- def test_byte_size_watchpoints_with_byte_selection(self):
+ @expectedFailureAll(archs="^riscv.*")
+ def test_byte_size_hardware_watchpoints_with_byte_selection(self):
"""Test to selectively watch different bytes in a 8-byte array."""
- self.run_watchpoint_size_test("byteArray", 8, "1")
+ self.run_watchpoint_size_test(
+ "byteArray", 8, "1", WatchpointType.READ_WRITE, lldb.eWatchpointModeHardware
+ )
+
+ def test_byte_size_software_watchpoints_with_byte_selection(self):
+ """Test to selectively watch different bytes in a 8-byte array."""
+ # The software watchpoints can only be of the modify type, so in this test,
+ # we will try to use modify type watchpoints instead of the ones used in the
+ # original test (read-write type).
+ self.run_watchpoint_size_test(
+ "byteArray", 8, "1", WatchpointType.MODIFY, lldb.eWatchpointModeSoftware
+ )
# Read-write watchpoints not supported on SystemZ
@expectedFailureAll(archs=["s390x"])
- def test_two_byte_watchpoints_with_word_selection(self):
+ @expectedFailureAll(archs="^riscv.*")
+ def test_two_byte_hardware_watchpoints_with_word_selection(self):
+ """Test to selectively watch different words in an 8-byte word array."""
+ self.run_watchpoint_size_test(
+ "wordArray", 4, "2", WatchpointType.READ_WRITE, lldb.eWatchpointModeHardware
+ )
+
+ def test_two_byte_software_watchpoints_with_word_selection(self):
"""Test to selectively watch different words in an 8-byte word array."""
- self.run_watchpoint_size_test("wordArray", 4, "2")
+ # The software watchpoints can only be of the modify type, so in this test,
+ # we will try to use modify type watchpoints instead of the ones used in the
+ # original test (read-write type).
+ self.run_watchpoint_size_test(
+ "wordArray", 4, "2", WatchpointType.MODIFY, lldb.eWatchpointModeSoftware
+ )
# Read-write watchpoints not supported on SystemZ
@expectedFailureAll(archs=["s390x"])
- def test_four_byte_watchpoints_with_dword_selection(self):
+ @expectedFailureAll(archs="^riscv.*")
+ def test_four_byte_hardware_watchpoints_with_dword_selection(self):
+ """Test to selectively watch two double words in an 8-byte dword array."""
+ self.run_watchpoint_size_test(
+ "dwordArray",
+ 2,
+ "4",
+ WatchpointType.READ_WRITE,
+ lldb.eWatchpointModeHardware,
+ )
+
+ def test_four_byte_software_watchpoints_with_dword_selection(self):
"""Test to selectively watch two double words in an 8-byte dword array."""
- self.run_watchpoint_size_test("dwordArray", 2, "4")
+ # The software watchpoints can only be of the modify type, so in this test,
+ # we will try to use modify type watchpoints instead of the ones used in the
+ # original test (read-write type).
+ self.run_watchpoint_size_test(
+ "dwordArray", 2, "4", WatchpointType.MODIFY, lldb.eWatchpointModeSoftware
+ )
- def run_watchpoint_size_test(self, arrayName, array_size, watchsize):
+ def run_watchpoint_size_test(
+ self, arrayName, array_size, watchsize, wp_type, wp_mode
+ ):
self.build(dictionary=self.d)
self.setTearDownCleanup(dictionary=self.d)
@@ -74,9 +117,13 @@ def run_watchpoint_size_test(self, arrayName, array_size, watchsize):
# Set a read_write type watchpoint arrayName
watch_loc = arrayName + "[" + str(i) + "]"
self.expect(
- "watchpoint set variable -w read_write " + watch_loc,
+ f"{get_set_watchpoint_CLI_command(WatchpointCLICommandVariant.VARIABLE, wp_type, wp_mode)} {arrayName}[{str(i)}]",
WATCHPOINT_CREATED,
- substrs=["Watchpoint created", "size = " + watchsize, "type = rw"],
+ substrs=[
+ "Watchpoint created",
+ f"size = {watchsize}",
+ f"type = {wp_type.value[0]}",
+ ],
)
# Use the '-v' option to do verbose listing of the watchpoint.
diff --git a/lldb/test/API/functionalities/watchpoint/large-watchpoint/TestLargeWatchpoint.py b/lldb/test/API/functionalities/watchpoint/large-watchpoint/TestLargeWatchpoint.py
index 8db242a17feb5..8fd222fbea992 100644
--- a/lldb/test/API/functionalities/watchpoint/large-watchpoint/TestLargeWatchpoint.py
+++ b/lldb/test/API/functionalities/watchpoint/large-watchpoint/TestLargeWatchpoint.py
@@ -8,6 +8,7 @@
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
+from lldbsuite.test.lldbwatchpointutils import *
class UnalignedWatchpointTestCase(TestBase):
@@ -27,7 +28,14 @@ def continue_and_report_stop_reason(self, process, iter_str):
# debugserver only gained the ability to watch larger regions
# with this patch.
- def test_large_watchpoint(self):
+ @skipIfOutOfTreeDebugserver
+ def test_large_hw_watchpoint(self):
+ self.do_large_watchpoint(WatchpointType.MODIFY, lldb.eWatchpointModeHardware)
+
+ def test_large_sw_watchpoint(self):
+ self.do_large_watchpoint(WatchpointType.MODIFY, lldb.eWatchpointModeSoftware)
+
+ def do_large_watchpoint(self, wp_type, wp_mode):
"""Test watchpoint that covers a large region of memory."""
self.build()
self.main_source_file = lldb.SBFileSpec("main.c")
@@ -44,9 +52,7 @@ def test_large_watchpoint(self):
# to a 1024 byte boundary to begin with, force alignment.
wa_256_addr = (array_addr + 1024) & ~(1024 - 1)
err = lldb.SBError()
- wp_opts = lldb.SBWatchpointOptions()
- wp_opts.SetWatchpointTypeWrite(lldb.eWatchpointWriteTypeOnModify)
- wp = target.WatchpointCreateByAddress(wa_256_addr, 1024, wp_opts, err)
+ wp = set_watchpoint_by_address(target, wa_256_addr, 1024, wp_type, wp_mode, err)
self.assertTrue(wp.IsValid())
self.assertSuccess(err)
diff --git a/lldb/test/API/functionalities/watchpoint/modify-watchpoints/TestModifyWatchpoint.py b/lldb/test/API/functionalities/watchpoint/modify-watchpoints/TestModifyWatchpoint.py
index b581969208da5..fd9bdf2e3a431 100644
--- a/lldb/test/API/functionalities/watchpoint/modify-watchpoints/TestModifyWatchpoint.py
+++ b/lldb/test/API/functionalities/watchpoint/modify-watchpoints/TestModifyWatchpoint.py
@@ -8,13 +8,21 @@
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
+from lldbsuite.test.lldbwatchpointutils import *
@skipIfWindows
class ModifyWatchpointTestCase(TestBase):
NO_DEBUG_INFO_TESTCASE = True
- def test_modify_watchpoint(self):
+ @expectedFailureAll(archs="^riscv.*")
+ def test_modify_hardware_watchpoint(self):
+ self.do_modify_watchpoint(WatchpointType.MODIFY, lldb.eWatchpointModeHardware)
+
+ def test_modify_software_watchpoint(self):
+ self.do_modify_watchpoint(WatchpointType.MODIFY, lldb.eWatchpointModeSoftware)
+
+ def do_modify_watchpoint(self, wp_type, wp_mode):
"""Test that a modify watchpoint only stops when the value changes."""
self.build()
self.main_source_file = lldb.SBFileSpec("main.c")
@@ -22,7 +30,9 @@ def test_modify_watchpoint(self):
self, "break here", self.main_source_file
)
- self.runCmd("watch set variable value")
+ self.runCmd(
+ f"{get_set_watchpoint_CLI_command(WatchpointCLICommandVariant.VARIABLE, wp_type, wp_mode)} value"
+ )
process.Continue()
frame = process.GetSelectedThread().GetFrameAtIndex(0)
self.assertEqual(frame.locals["value"][0].GetValueAsUnsigned(), 10)
diff --git a/lldb/test/API/functionalities/watchpoint/unaligned-large-watchpoint/TestUnalignedLargeWatchpoint.py b/lldb/test/API/functionalities/watchpoint/unaligned-large-watchpoint/TestUnalignedLargeWatchpoint.py
index c8ec5cfb03dba..8a6df1e5529a2 100644
--- a/lldb/test/API/functionalities/watchpoint/unaligned-large-watchpoint/TestUnalignedLargeWatchpoint.py
+++ b/lldb/test/API/functionalities/watchpoint/unaligned-large-watchpoint/TestUnalignedLargeWatchpoint.py
@@ -9,6 +9,7 @@
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
+from lldbsuite.test.lldbwatchpointutils import *
class UnalignedLargeWatchpointTestCase(TestBase):
@@ -31,7 +32,17 @@ def continue_and_report_stop_reason(self, process, iter_str):
# Test on 64-bit targets where we probably have
# four watchpoint registers that can watch doublewords (8-byte).
@skipIf(archs=no_match(["arm64", "arm64e", "aarch64", "x86_64"]))
- def test_unaligned_large_watchpoint(self):
+ def test_unaligned_large_hw_watchpoint(self):
+ self.do_unaligned_large_watchpoint(
+ WatchpointType.MODIFY, lldb.eWatchpointModeHardware
+ )
+
+ def test_unaligned_large_sw_watchpoint(self):
+ self.do_unaligned_large_watchpoint(
+ WatchpointType.MODIFY, lldb.eWatchpointModeSoftware
+ )
+
+ def do_unaligned_large_watchpoint(self, wp_type, wp_mode):
"""Test watching an unaligned region of memory that requires multiple watchpoints."""
self.build()
self.main_source_file = lldb.SBFileSpec("main.c")
@@ -54,9 +65,7 @@ def test_unaligned_large_watchpoint(self):
wa_addr = wa_addr + 7
err = lldb.SBError()
- wp_opts = lldb.SBWatchpointOptions()
- wp_opts.SetWatchpointTypeWrite(lldb.eWatchpointWriteTypeOnModify)
- wp = target.WatchpointCreateByAddress(wa_addr, 22, wp_opts, err)
+ wp = set_watchpoint_by_address(target, wa_addr, 22, wp_type, wp_mode, err)
self.assertTrue(wp.IsValid())
self.assertSuccess(err)
if self.TraceOn():
@@ -79,9 +88,13 @@ def test_unaligned_large_watchpoint(self):
# Now try watching a 16 byte variable
# (not unaligned, but a good check to do anyway)
- frame = thread.GetFrameAtIndex(0)
+ frame0 = thread.GetFrameAtIndex(0)
+ value = frame0.FindValue("variable", lldb.eValueTypeVariableLocal)
err = lldb.SBError()
- wp = frame.locals["variable"][0].Watch(True, False, True, err)
+ wp = set_watchpoint_at_value(value, wp_type, wp_mode, err)
+ self.assertTrue(
+ value and wp, "Successfully found the variable and set a watchpoint"
+ )
self.assertSuccess(err)
if self.TraceOn():
self.runCmd("frame select 0")
diff --git a/lldb/test/API/functionalities/watchpoint/unaligned-large-watchpoint/main.c b/lldb/test/API/functionalities/watchpoint/unaligned-large-watchpoint/main.c
index 4c8c66bfc6762..44d5d1700d6e1 100644
--- a/lldb/test/API/functionalities/watchpoint/unaligned-large-watchpoint/main.c
+++ b/lldb/test/API/functionalities/watchpoint/unaligned-large-watchpoint/main.c
@@ -11,7 +11,7 @@ struct obj {
};
int main() {
- const int count = 16776960;
+ const int count = 1000;
uint8_t *array = (uint8_t *)malloc(count);
memset(array, 0, count);
struct obj variable;
diff --git a/lldb/test/API/functionalities/watchpoint/unaligned-spanning-two-dwords/TestUnalignedSpanningDwords.py b/lldb/test/API/functionalities/watchpoint/unaligned-spanning-two-dwords/TestUnalignedSpanningDwords.py
index bf0cb24e21888..477d530fe8683 100644
--- a/lldb/test/API/functionalities/watchpoint/unaligned-spanning-two-dwords/TestUnalignedSpanningDwords.py
+++ b/lldb/test/API/functionalities/watchpoint/unaligned-spanning-two-dwords/TestUnalignedSpanningDwords.py
@@ -10,6 +10,7 @@
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
+from lldbsuite.test.lldbwatchpointutils import *
class UnalignedWatchpointTestCase(TestBase):
@@ -32,7 +33,17 @@ def hit_watchpoint_and_continue(self, process, iter_str):
# older debugservers will return the base address of the doubleword
# which lldb doesn't understand, and will stop executing without a
# proper stop reason.
- def test_unaligned_watchpoint(self):
+ def test_unaligned_hw_watchpoint(self):
+ self.do_unaligned_watchpoint(
+ WatchpointType.MODIFY, lldb.eWatchpointModeHardware
+ )
+
+ def test_unaligned_sw_watchpoint(self):
+ self.do_unaligned_watchpoint(
+ WatchpointType.MODIFY, lldb.eWatchpointModeSoftware
+ )
+
+ def do_unaligned_watchpoint(self, wp_type, wp_mode):
"""Test a watchpoint that is handled by two hardware watchpoint registers."""
self.build()
self.main_source_file = lldb.SBFileSpec("main.c")
@@ -47,9 +58,9 @@ def test_unaligned_watchpoint(self):
a_bytebuf_6 = frame.GetValueForVariablePath("a.bytebuf[6]")
a_bytebuf_6_addr = a_bytebuf_6.GetAddress().GetLoadAddress(target)
err = lldb.SBError()
- wp_opts = lldb.SBWatchpointOptions()
- wp_opts.SetWatchpointTypeWrite(lldb.eWatchpointWriteTypeOnModify)
- wp = target.WatchpointCreateByAddress(a_bytebuf_6_addr, 4, wp_opts, err)
+ wp = set_watchpoint_by_address(
+ target, a_bytebuf_6_addr, 4, wp_type, wp_mode, err
+ )
self.assertTrue(err.Success())
self.assertTrue(wp.IsEnabled())
self.assertEqual(wp.GetWatchSize(), 4)
diff --git a/lldb/test/API/python_api/default-constructor/sb_value.py b/lldb/test/API/python_api/default-constructor/sb_value.py
index 9e31a70a79ba4..08a0c32c2941e 100644
--- a/lldb/test/API/python_api/default-constructor/sb_value.py
+++ b/lldb/test/API/python_api/default-constructor/sb_value.py
@@ -34,9 +34,14 @@ def fuzz_obj(obj):
obj.GetDescription(stream)
obj.GetExpressionPath(stream)
obj.GetExpressionPath(stream, True)
+ wp_opts = lldb.SBWatchpointOptions()
+ wp_opts.SetWatchpointMode(lldb.eWatchpointModeHardware)
+ wp_opts.SetWatchpointTypeRead(True)
error = lldb.SBError()
- obj.Watch(True, True, False, error)
- obj.WatchPointee(True, False, True, error)
+ obj.Watch(True, wp_opts, error)
+ wp_opts.SetWatchpointTypeRead(False)
+ wp_opts.SetWatchpointTypeWrite(lldb.eWatchpointWriteTypeOnModify)
+ obj.WatchPointee(True, wp_opts, error)
for child_val in obj:
s = str(child_val)
error = lldb.SBError()
diff --git a/lldb/test/API/python_api/watchpoint/TestSetWatchpoint.py b/lldb/test/API/python_api/watchpoint/TestSetWatchpoint.py
index 94b0278ee099c..1ea20469a62fc 100644
--- a/lldb/test/API/python_api/watchpoint/TestSetWatchpoint.py
+++ b/lldb/test/API/python_api/watchpoint/TestSetWatchpoint.py
@@ -6,6 +6,7 @@
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
+from lldbsuite.test.lldbwatchpointutils import *
class SetWatchpointAPITestCase(TestBase):
@@ -22,20 +23,53 @@ def setUp(self):
# Read-write watchpoints not supported on SystemZ
@expectedFailureAll(archs=["s390x"])
- def test_watch_val(self):
+ @expectedFailureAll(archs="^riscv.*")
+ def test_hardware_watch_val(self):
"""Exercise SBValue.Watch() API to set a watchpoint."""
- self._test_watch_val(variable_watchpoint=False)
- pass
+ self._test_watch_val(
+ WatchpointType.READ_WRITE,
+ lldb.eWatchpointModeHardware,
+ variable_watchpoint=False,
+ )
+
+ def test_software_watch_val(self):
+ """Exercise SBValue.Watch() API to set a watchpoint."""
+ self.runCmd("settings append target.env-vars SW_WP_CASE=YES")
+ self._test_watch_val(
+ WatchpointType.MODIFY,
+ lldb.eWatchpointModeSoftware,
+ variable_watchpoint=False,
+ )
+ self.runCmd("settings clear target.env-vars")
+ # Read-write watchpoints not supported on SystemZ
@expectedFailureAll(archs=["s390x"])
- def test_watch_variable(self):
+ @expectedFailureAll(archs="^riscv.*")
+ def test_hardware_watch_variable(self):
+ """
+ Exercise some watchpoint APIs when the watchpoint
+ is created as a variable watchpoint.
+ """
+ self._test_watch_val(
+ WatchpointType.READ_WRITE,
+ lldb.eWatchpointModeHardware,
+ variable_watchpoint=True,
+ )
+
+ def test_software_watch_variable(self):
"""
Exercise some watchpoint APIs when the watchpoint
is created as a variable watchpoint.
"""
- self._test_watch_val(variable_watchpoint=True)
+ self.runCmd("settings append target.env-vars SW_WP_CASE=YES")
+ self._test_watch_val(
+ WatchpointType.MODIFY,
+ lldb.eWatchpointModeSoftware,
+ variable_watchpoint=True,
+ )
+ self.runCmd("settings clear target.env-vars")
- def _test_watch_val(self, variable_watchpoint):
+ def _test_watch_val(self, wp_type, wp_mode, variable_watchpoint):
exe = self.getBuildArtifact("a.out")
# Create a target by the debugger.
@@ -61,7 +95,9 @@ def _test_watch_val(self, variable_watchpoint):
if variable_watchpoint:
# FIXME: There should probably be an API to create a
# variable watchpoint.
- self.runCmd("watchpoint set variable -w read_write -- global")
+ self.runCmd(
+ f"{get_set_watchpoint_CLI_command(WatchpointCLICommandVariant.VARIABLE, wp_type, wp_mode)} -- global"
+ )
watchpoint = target.GetWatchpointAtIndex(0)
self.assertEqual(
watchpoint.GetWatchValueKind(), lldb.eWatchPointValueKindVariable
@@ -75,7 +111,7 @@ def _test_watch_val(self, variable_watchpoint):
else:
value = frame0.FindValue("global", lldb.eValueTypeVariableGlobal)
error = lldb.SBError()
- watchpoint = value.Watch(True, True, True, error)
+ watchpoint = set_watchpoint_at_value(value, wp_type, wp_mode, error)
self.assertTrue(
value and watchpoint,
"Successfully found the variable and set a watchpoint",
@@ -88,12 +124,16 @@ def _test_watch_val(self, variable_watchpoint):
# is reported as eWatchPointValueKindExpression. If the kind is
# actually supposed to be eWatchPointValueKindVariable then the spec
# should probably be 'global'.
- self.assertEqual(watchpoint.GetWatchSpec(), None)
+ self.assertEqual(watchpoint.GetWatchSpec(), "global")
self.assertEqual(watchpoint.GetType().GetDisplayTypeName(), "int32_t")
self.assertEqual(value.GetName(), "global")
self.assertEqual(value.GetType(), watchpoint.GetType())
- self.assertTrue(watchpoint.IsWatchingReads())
+ self.assertTrue(
+ watchpoint.IsWatchingReads()
+ if wp_mode == lldb.eWatchpointModeHardware
+ else not watchpoint.IsWatchingReads()
+ )
self.assertTrue(watchpoint.IsWatchingWrites())
# Hide stdout if not running with '-t' option.
diff --git a/lldb/test/API/python_api/watchpoint/TestWatchpointIgnoreCount.py b/lldb/test/API/python_api/watchpoint/TestWatchpointIgnoreCount.py
index ab0e9f00a65b0..616377d6d49ad 100644
--- a/lldb/test/API/python_api/watchpoint/TestWatchpointIgnoreCount.py
+++ b/lldb/test/API/python_api/watchpoint/TestWatchpointIgnoreCount.py
@@ -6,6 +6,7 @@
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
+from lldbsuite.test.lldbwatchpointutils import *
class WatchpointIgnoreCountTestCase(TestBase):
@@ -30,7 +31,20 @@ def affected_by_radar_93863107(self):
# Read-write watchpoints not supported on SystemZ
@expectedFailureAll(archs=["s390x"])
- def test_set_watch_ignore_count(self):
+ @expectedFailureAll(archs="^riscv.*")
+ def test_set_hw_watch_ignore_count(self):
+ self.do_set_watch_ignore_count(
+ WatchpointType.READ_WRITE, lldb.eWatchpointModeHardware
+ )
+
+ def test_set_sw_watch_ignore_count(self):
+ self.runCmd("settings append target.env-vars SW_WP_CASE=YES")
+ self.do_set_watch_ignore_count(
+ WatchpointType.MODIFY, lldb.eWatchpointModeSoftware
+ )
+ self.runCmd("settings clear target.env-vars")
+
+ def do_set_watch_ignore_count(self, wp_type, wp_mode):
"""Test SBWatchpoint.SetIgnoreCount() API."""
self.build()
exe = self.getBuildArtifact("a.out")
@@ -57,7 +71,7 @@ def test_set_watch_ignore_count(self):
# Watch 'global' for read and write.
value = frame0.FindValue("global", lldb.eValueTypeVariableGlobal)
error = lldb.SBError()
- watchpoint = value.Watch(True, True, True, error)
+ watchpoint = set_watchpoint_at_value(value, wp_type, wp_mode, error)
self.assertTrue(
value and watchpoint, "Successfully found the variable and set a watchpoint"
)
diff --git a/lldb/test/API/python_api/watchpoint/TestWatchpointIter.py b/lldb/test/API/python_api/watchpoint/TestWatchpointIter.py
index f7c22fb9c2bd6..435ea0a7f81b1 100644
--- a/lldb/test/API/python_api/watchpoint/TestWatchpointIter.py
+++ b/lldb/test/API/python_api/watchpoint/TestWatchpointIter.py
@@ -6,6 +6,7 @@
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
+from lldbsuite.test.lldbwatchpointutils import *
class WatchpointIteratorTestCase(TestBase):
@@ -25,7 +26,16 @@ def setUp(self):
# Find the line number to break inside main().
self.line = line_number(self.source, "// Set break point at this line.")
- def test_watch_iter(self):
+ @expectedFailureAll(archs="^riscv.*")
+ def test_hw_watch_iter(self):
+ self.do_watch_iter(WatchpointType.READ_WRITE, lldb.eWatchpointModeHardware)
+
+ def test_sw_watch_iter(self):
+ self.runCmd("settings append target.env-vars SW_WP_CASE=YES")
+ self.do_watch_iter(WatchpointType.MODIFY, lldb.eWatchpointModeSoftware)
+ self.runCmd("settings clear target.env-vars")
+
+ def do_watch_iter(self, wp_type, wp_mode):
"""Exercise SBTarget.watchpoint_iter() API to iterate on the available watchpoints."""
self.build()
exe = self.getBuildArtifact("a.out")
@@ -52,7 +62,7 @@ def test_watch_iter(self):
# Watch 'global' for read and write.
value = frame0.FindValue("global", lldb.eValueTypeVariableGlobal)
error = lldb.SBError()
- watchpoint = value.Watch(True, False, True, error)
+ watchpoint = set_watchpoint_at_value(value, wp_type, wp_mode, error)
self.assertTrue(
value and watchpoint, "Successfully found the variable and set a watchpoint"
)
diff --git a/lldb/test/API/python_api/watchpoint/condition/TestWatchpointConditionAPI.py b/lldb/test/API/python_api/watchpoint/condition/TestWatchpointConditionAPI.py
index 5de75834e73b6..f9bf4c557439f 100644
--- a/lldb/test/API/python_api/watchpoint/condition/TestWatchpointConditionAPI.py
+++ b/lldb/test/API/python_api/watchpoint/condition/TestWatchpointConditionAPI.py
@@ -6,6 +6,7 @@
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
+from lldbsuite.test.lldbwatchpointutils import *
class WatchpointConditionAPITestCase(TestBase):
@@ -25,7 +26,14 @@ def setUp(self):
self.exe_name = self.testMethodName
self.d = {"CXX_SOURCES": self.source, "EXE": self.exe_name}
- def test_watchpoint_cond_api(self):
+ @expectedFailureAll(archs="^riscv.*")
+ def test_hw_watchpoint_cond_api(self):
+ self.do_watchpoint_cond_api(WatchpointType.MODIFY, lldb.eWatchpointModeHardware)
+
+ def test_sw_watchpoint_cond_api(self):
+ self.do_watchpoint_cond_api(WatchpointType.MODIFY, lldb.eWatchpointModeSoftware)
+
+ def do_watchpoint_cond_api(self, wp_type, wp_mode):
"""Test watchpoint condition API."""
self.build(dictionary=self.d)
self.setTearDownCleanup(dictionary=self.d)
@@ -53,7 +61,7 @@ def test_watchpoint_cond_api(self):
# Watch 'global' for write.
value = frame0.FindValue("global", lldb.eValueTypeVariableGlobal)
error = lldb.SBError()
- watchpoint = value.Watch(True, False, True, error)
+ watchpoint = set_watchpoint_at_value(value, wp_type, wp_mode, error)
self.assertTrue(
value and watchpoint, "Successfully found the variable and set a watchpoint"
)
diff --git a/lldb/test/API/python_api/watchpoint/main.c b/lldb/test/API/python_api/watchpoint/main.c
index 80d35a920d67a..8ba1f072ef2ff 100644
--- a/lldb/test/API/python_api/watchpoint/main.c
+++ b/lldb/test/API/python_api/watchpoint/main.c
@@ -1,5 +1,6 @@
-#include <stdio.h>
#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
int32_t global = 10; // Watchpoint variable declaration.
@@ -12,5 +13,10 @@ int main(int argc, char** argv) {
local += argc;
++local;
printf("local: %d\n", local);
- printf("global=%d\n", global);
+
+ const char *s = getenv("SW_WP_CASE");
+ if (s == NULL)
+ printf("global=%d\n", global);
+ else
+ global = 30;
}
diff --git a/lldb/test/API/python_api/watchpoint/watchlocation/TestSetWatchlocation.py b/lldb/test/API/python_api/watchpoint/watchlocation/TestSetWatchlocation.py
index cae01db06eefd..4174471b19c56 100644
--- a/lldb/test/API/python_api/watchpoint/watchlocation/TestSetWatchlocation.py
+++ b/lldb/test/API/python_api/watchpoint/watchlocation/TestSetWatchlocation.py
@@ -6,6 +6,7 @@
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
+from lldbsuite.test.lldbwatchpointutils import *
class SetWatchlocationAPITestCase(TestBase):
@@ -22,7 +23,15 @@ def setUp(self):
self.violating_func = "do_bad_thing_with_location"
@skipIfWindows # This test is flaky on Windows
- def test_watch_location(self):
+ @expectedFailureAll(archs="^riscv.*")
+ def test_hw_watch_location(self):
+ self.do_watch_location(WatchpointType.MODIFY, lldb.eWatchpointModeHardware)
+
+ @skip
+ def test_sw_watch_location(self):
+ self.do_watch_location(WatchpointType.MODIFY, lldb.eWatchpointModeSoftware)
+
+ def do_watch_location(self, wp_type, wp_mode):
"""Exercise SBValue.WatchPointee() API to set a watchpoint."""
self.build()
exe = self.getBuildArtifact("a.out")
@@ -52,7 +61,7 @@ def test_watch_location(self):
)
# Watch for write to *g_char_ptr.
error = lldb.SBError()
- watchpoint = value.WatchPointee(True, False, True, error)
+ watchpoint = set_watchpoint_at_pointee(value, wp_type, wp_mode, error)
self.assertTrue(
value and watchpoint, "Successfully found the pointer and set a watchpoint"
)
@@ -64,7 +73,7 @@ def test_watch_location(self):
watchpoint.GetWatchValueKind(), lldb.eWatchPointValueKindExpression
)
# FIXME: The spec should probably be 'g_char_ptr'
- self.assertEqual(watchpoint.GetWatchSpec(), None)
+ self.assertEqual(watchpoint.GetWatchSpec(), "*::g_char_ptr")
self.assertEqual(watchpoint.GetType().GetDisplayTypeName(), "char")
self.assertFalse(watchpoint.IsWatchingReads())
self.assertTrue(watchpoint.IsWatchingWrites())
diff --git a/lldb/test/API/python_api/watchpoint/watchlocation/TestTargetWatchAddress.py b/lldb/test/API/python_api/watchpoint/watchlocation/TestTargetWatchAddress.py
index f1c7a60300df5..4e64e8d33881a 100644
--- a/lldb/test/API/python_api/watchpoint/watchlocation/TestTargetWatchAddress.py
+++ b/lldb/test/API/python_api/watchpoint/watchlocation/TestTargetWatchAddress.py
@@ -6,6 +6,7 @@
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
+from lldbsuite.test.lldbwatchpointutils import *
class TargetWatchpointCreateByAddressPITestCase(TestBase):
@@ -26,82 +27,16 @@ def setUp(self):
archs=["x86_64"],
bugnumber="github.com/llvm/llvm-project/issues/144777",
)
- def test_watch_create_by_address(self):
- """Exercise SBTarget.WatchpointCreateByAddress() API to set a watchpoint."""
- self.build()
- exe = self.getBuildArtifact("a.out")
-
- # Create a target by the debugger.
- target = self.dbg.CreateTarget(exe)
- self.assertTrue(target, VALID_TARGET)
-
- # Now create a breakpoint on main.c.
- breakpoint = target.BreakpointCreateByLocation(self.source, self.line)
- self.assertTrue(
- breakpoint and breakpoint.GetNumLocations() == 1, VALID_BREAKPOINT
- )
-
- # Now launch the process, and do not stop at the entry point.
- process = target.LaunchSimple(None, None, self.get_process_working_directory())
-
- # We should be stopped due to the breakpoint. Get frame #0.
- process = target.GetProcess()
- self.assertState(process.GetState(), lldb.eStateStopped, PROCESS_STOPPED)
- thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonBreakpoint)
- frame0 = thread.GetFrameAtIndex(0)
-
- value = frame0.FindValue("g_char_ptr", lldb.eValueTypeVariableGlobal)
- pointee = value.CreateValueFromAddress(
- "pointee", value.GetValueAsUnsigned(0), value.GetType().GetPointeeType()
- )
- # Watch for write to *g_char_ptr.
- error = lldb.SBError()
- wp_opts = lldb.SBWatchpointOptions()
- wp_opts.SetWatchpointTypeWrite(lldb.eWatchpointWriteTypeOnModify)
- watchpoint = target.WatchpointCreateByAddress(
- value.GetValueAsUnsigned(), 1, wp_opts, error
- )
- self.assertTrue(
- value and watchpoint, "Successfully found the pointer and set a watchpoint"
- )
- self.DebugSBValue(value)
- self.DebugSBValue(pointee)
+ @expectedFailureAll(archs="^riscv.*")
+ def test_hw_watch_address(self):
+ self.do_watch_address(WatchpointType.MODIFY, lldb.eWatchpointModeHardware)
- # Hide stdout if not running with '-t' option.
- if not self.TraceOn():
- self.HideStdout()
-
- print(watchpoint)
-
- # Continue. Expect the program to stop due to the variable being
- # written to.
- process.Continue()
+ @skip
+ def test_sw_watch_address(self):
+ self.do_watch_address(WatchpointType.MODIFY, lldb.eWatchpointModeSoftware)
- if self.TraceOn():
- lldbutil.print_stacktraces(process)
-
- thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonWatchpoint)
- self.assertTrue(thread, "The thread stopped due to watchpoint")
- self.DebugSBValue(value)
- self.DebugSBValue(pointee)
-
- self.expect(
- lldbutil.print_stacktrace(thread, string_buffer=True),
- exe=False,
- substrs=[self.violating_func],
- )
-
- # This finishes our test.
-
- @skipIf(
- oslist=["windows"],
- archs=["x86_64"],
- bugnumber="github.com/llvm/llvm-project/issues/144777",
- )
- def test_watch_address(self):
- """Exercise SBTarget.WatchAddress() API to set a watchpoint.
- Same as test_watch_create_by_address, but uses the simpler API.
- """
+ def do_watch_address(self, wp_type, wp_mode):
+ """Exercise SBTarget.WatchpointCreateByAddress() API to set a watchpoint."""
self.build()
exe = self.getBuildArtifact("a.out")
@@ -130,10 +65,8 @@ def test_watch_address(self):
)
# Watch for write to *g_char_ptr.
error = lldb.SBError()
- watch_read = False
- watch_write = True
- watchpoint = target.WatchAddress(
- value.GetValueAsUnsigned(), 1, watch_read, watch_write, error
+ watchpoint = set_watchpoint_by_address(
+ target, value.GetValueAsUnsigned(), 1, wp_type, wp_mode, error
)
self.assertTrue(
value and watchpoint, "Successfully found the pointer and set a watchpoint"
@@ -175,7 +108,18 @@ def test_watch_address(self):
archs=["x86_64"],
bugnumber="github.com/llvm/llvm-project/issues/142196",
)
- def test_watch_address_with_invalid_watch_size(self):
+ @expectedFailureAll(archs="^riscv.*")
+ def test_hw_watch_address_with_invalid_watch_size(self):
+ self.do_watch_address_with_invalid_watch_size(
+ WatchpointType.MODIFY, lldb.eWatchpointModeHardware
+ )
+
+ def test_sw_watch_address_with_invalid_watch_size(self):
+ self.do_watch_address_with_invalid_watch_size(
+ WatchpointType.MODIFY, lldb.eWatchpointModeSoftware
+ )
+
+ def do_watch_address_with_invalid_watch_size(self, wp_type, wp_mode):
"""Exercise SBTarget.WatchpointCreateByAddress() API but pass an invalid watch_size."""
self.build()
exe = self.getBuildArtifact("a.out")
@@ -207,13 +151,13 @@ def test_watch_address_with_invalid_watch_size(self):
# debugserver on Darwin AArch64 systems can watch large regions
# of memory via https://reviews.llvm.org/D149792 , don't run this
# test there.
- if self.getArchitecture() not in ["arm64", "arm64e", "arm64_32"]:
+ if wp_mode == lldb.eWatchpointModeHardware and (
+ self.getArchitecture() not in ["arm64", "arm64e", "arm64_32"]
+ ):
# Watch for write to *g_char_ptr.
error = lldb.SBError()
- wp_opts = lldb.SBWatchpointOptions()
- wp_opts.SetWatchpointTypeWrite(lldb.eWatchpointWriteTypeOnModify)
- watchpoint = target.WatchpointCreateByAddress(
- value.GetValueAsUnsigned(), 365, wp_opts, error
+ watchpoint = set_watchpoint_by_address(
+ target, value.GetValueAsUnsigned(), 365, wp_type, wp_mode, error
)
self.assertFalse(watchpoint)
self.expect(
>From 8189d590e71984243923922c8d974c6f55613c0e Mon Sep 17 00:00:00 2001
From: Daniil Avdeev <daniil.avdeev at syntacore.com>
Date: Tue, 29 Jul 2025 16:00:58 +0000
Subject: [PATCH 4/5] [lldb] enable ThreadPlanStepOut internal breakpoint in
any case
Commit cbb4e99f3613549c2168f52d6f348e3a7ee3cf55 introduced a regression
in lldb software watchpoint tests.
The issue occurs in the following scenario:
1. Set a software watchpoint
2. Step over a function call
3. The watchpoint triggers inside the function being stepped over
4. After continuing execution, we expect to stop after returning
from
the function (as with a normal step-over operation)
However, with the mentioned patch, execution no longer stops at the end
of the function. The patch seems to have modified ThreadPlanStepOut's
stop condition - it now only stops when the stop reason is
eStopReasonBreakpoint.
The problem occurs because ThreadPlanStepOut only enables its internal
breakpoint when it's at the top of the ThreadPlanStack. With software
watchpoints, execution proceeds step-by-step while checking if any
watched values change after each step. In this case,
ThreadPlanWatchpointStepInstruction remains higher on the stack than
ThreadPlanStepOut, therefore the internal breakpoint stays disabled. As
a result, we stop due to eStopReasonTrace instead of the desired
eStopReasonBreakpoint, and consequently fail to stop at the intended
location.
This patch removes the requirement for ThreadPlanStepOut to be the
current plan and enables the internal breakpoint unconditionally. I
believe this check is legacy code that can be safely removed - I was
quite surprised to find this breakpoint isn't always enabled by default.
However, if issues arise, we could consider modifying the condition
introduced by the patch. For example, instead of checking the stop
reason, we could verify whether the stop address matches the target
address of the breakpoint that ThreadPlanStepOut uses to stop at the end
of the step.
---
lldb/source/Target/ThreadPlanStepOut.cpp | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/lldb/source/Target/ThreadPlanStepOut.cpp b/lldb/source/Target/ThreadPlanStepOut.cpp
index 0628451a5abf9..50f4db7df521e 100644
--- a/lldb/source/Target/ThreadPlanStepOut.cpp
+++ b/lldb/source/Target/ThreadPlanStepOut.cpp
@@ -440,11 +440,10 @@ bool ThreadPlanStepOut::DoWillResume(StateType resume_state,
if (m_return_bp_id == LLDB_INVALID_BREAK_ID)
return false;
- if (current_plan) {
- Breakpoint *return_bp = GetTarget().GetBreakpointByID(m_return_bp_id).get();
- if (return_bp != nullptr)
- return_bp->SetEnabled(true);
- }
+ Breakpoint *return_bp = GetTarget().GetBreakpointByID(m_return_bp_id).get();
+ if (return_bp != nullptr)
+ return_bp->SetEnabled(true);
+
return true;
}
>From 343b7494ae40ecfe8462d90b5325097b6d8822dc Mon Sep 17 00:00:00 2001
From: Daniil Avdeev <daniil.avdeev at syntacore.com>
Date: Tue, 29 Jul 2025 16:02:07 +0000
Subject: [PATCH 5/5] [lldb] report watchpoint scope leaving
Currently, lldb silently disables watchpoints on local variables when
execution leaves the variable's scope.
This patch makes lldb emit a warning to the user when a watchpoint
leaves its scope.
---
lldb/source/Breakpoint/Watchpoint.cpp | 4 ++
.../Makefile | 3 ++
.../TestVariableWatchpointScopeLeaving.py | 45 +++++++++++++++++++
.../variable_watchpoint_scope_leaving/main.c | 6 +++
4 files changed, 58 insertions(+)
create mode 100644 lldb/test/API/commands/watchpoints/variable_watchpoint_scope_leaving/Makefile
create mode 100644 lldb/test/API/commands/watchpoints/variable_watchpoint_scope_leaving/TestVariableWatchpointScopeLeaving.py
create mode 100644 lldb/test/API/commands/watchpoints/variable_watchpoint_scope_leaving/main.c
diff --git a/lldb/source/Breakpoint/Watchpoint.cpp b/lldb/source/Breakpoint/Watchpoint.cpp
index 4ca0e655edcb4..6b9d5e50279aa 100644
--- a/lldb/source/Breakpoint/Watchpoint.cpp
+++ b/lldb/source/Breakpoint/Watchpoint.cpp
@@ -248,6 +248,10 @@ bool Watchpoint::VariableWatchpointDisabler(void *baton,
" matched internal breakpoint execution context",
watch_sp->GetID());
process_sp->DisableWatchpoint(watch_sp);
+ Debugger::ReportWarning(
+ llvm::formatv("Watchpoint {0} is leaving its scope! "
+ "Disabling this watchpoint.",
+ watch_sp->GetID()));
return false;
}
LLDB_LOGF(log,
diff --git a/lldb/test/API/commands/watchpoints/variable_watchpoint_scope_leaving/Makefile b/lldb/test/API/commands/watchpoints/variable_watchpoint_scope_leaving/Makefile
new file mode 100644
index 0000000000000..10495940055b6
--- /dev/null
+++ b/lldb/test/API/commands/watchpoints/variable_watchpoint_scope_leaving/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+
+include Makefile.rules
diff --git a/lldb/test/API/commands/watchpoints/variable_watchpoint_scope_leaving/TestVariableWatchpointScopeLeaving.py b/lldb/test/API/commands/watchpoints/variable_watchpoint_scope_leaving/TestVariableWatchpointScopeLeaving.py
new file mode 100644
index 0000000000000..38123c2372f77
--- /dev/null
+++ b/lldb/test/API/commands/watchpoints/variable_watchpoint_scope_leaving/TestVariableWatchpointScopeLeaving.py
@@ -0,0 +1,45 @@
+"""Test that variable watchpoints emit message leaving its scope."""
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import lldbutil
+from lldbsuite.test.lldbwatchpointutils import *
+
+
+class TestVariableWatchpointScopeLeaving(TestBase):
+ def setUp(self):
+ # Call super's setUp().
+ TestBase.setUp(self)
+
+ def do_test(self, wp_type, wp_mode):
+ self.build()
+ (target, process, cur_thread, bkpt) = lldbutil.run_to_source_breakpoint(
+ self, "// break here", lldb.SBFileSpec("main.c")
+ )
+
+ # Set a watchpoint for 'local'
+ self.expect(
+ f"{get_set_watchpoint_CLI_command(WatchpointCLICommandVariant.VARIABLE, wp_type, wp_mode)} local",
+ WATCHPOINT_CREATED,
+ substrs=[
+ "Watchpoint created",
+ "size = 4",
+ f"type = {wp_type.value[0]}",
+ ],
+ )
+
+ # Resume process execution. We should return from the function and notify the user that the watchpoint
+ # has left its scope.
+ self.runCmd("process continue")
+ self.assertIn(
+ self.res.GetError(),
+ "warning: Watchpoint 1 is leaving its scope! Disabling this watchpoint.",
+ )
+
+ @expectedFailureAll(archs="^riscv.*")
+ def test_hardware_variable_watchpoint(self):
+ self.do_test(WatchpointType.MODIFY, lldb.eWatchpointModeHardware)
+
+ def test_software_variable_watchpoint(self):
+ self.do_test(WatchpointType.MODIFY, lldb.eWatchpointModeSoftware)
diff --git a/lldb/test/API/commands/watchpoints/variable_watchpoint_scope_leaving/main.c b/lldb/test/API/commands/watchpoints/variable_watchpoint_scope_leaving/main.c
new file mode 100644
index 0000000000000..bf4cfc0dded84
--- /dev/null
+++ b/lldb/test/API/commands/watchpoints/variable_watchpoint_scope_leaving/main.c
@@ -0,0 +1,6 @@
+void foo() {
+ int local = 0;
+ return; // break here
+}
+
+int main() { foo(); }
More information about the lldb-commits
mailing list