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 m_opaque_up; 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, 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(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, 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, 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; 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, // 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, return m_watchpoint_resource_list; } + llvm::SmallVector 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, 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, 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/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/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 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 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; +} diff --git a/lldb/source/Breakpoint/Watchpoint.cpp b/lldb/source/Breakpoint/Watchpoint.cpp index f1366ca538075..6b9d5e50279aa 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; @@ -168,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, @@ -184,72 +268,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(&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 +390,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 +407,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 +480,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 +549,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(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(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 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 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 Process::GetEnabledSoftwareWatchpoint() { + llvm::SmallVector 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 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 = ""; - 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 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 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 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(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(*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(size), kind); - std::unique_lock 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(*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(*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) { 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; } 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/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..104cae31991ef --- /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(); } 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 #include +#include +#include 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 #include +#include +#include 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(