15ffd83dbSDimitry Andric //===-- Watchpoint.cpp ----------------------------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric
90b57cec5SDimitry Andric #include "lldb/Breakpoint/Watchpoint.h"
100b57cec5SDimitry Andric
110b57cec5SDimitry Andric #include "lldb/Breakpoint/StoppointCallbackContext.h"
125f757f3fSDimitry Andric #include "lldb/Breakpoint/WatchpointResource.h"
130b57cec5SDimitry Andric #include "lldb/Core/Value.h"
140b57cec5SDimitry Andric #include "lldb/Core/ValueObject.h"
150b57cec5SDimitry Andric #include "lldb/Core/ValueObjectMemory.h"
165f757f3fSDimitry Andric #include "lldb/DataFormatters/DumpValueObjectOptions.h"
170b57cec5SDimitry Andric #include "lldb/Expression/UserExpression.h"
189dba64beSDimitry Andric #include "lldb/Symbol/TypeSystem.h"
190b57cec5SDimitry Andric #include "lldb/Target/Process.h"
200b57cec5SDimitry Andric #include "lldb/Target/Target.h"
210b57cec5SDimitry Andric #include "lldb/Target/ThreadSpec.h"
2281ad6265SDimitry Andric #include "lldb/Utility/LLDBLog.h"
239dba64beSDimitry Andric #include "lldb/Utility/Log.h"
240b57cec5SDimitry Andric #include "lldb/Utility/Stream.h"
250b57cec5SDimitry Andric
260b57cec5SDimitry Andric using namespace lldb;
270b57cec5SDimitry Andric using namespace lldb_private;
280b57cec5SDimitry Andric
Watchpoint(Target & target,lldb::addr_t addr,uint32_t size,const CompilerType * type,bool hardware)290b57cec5SDimitry Andric Watchpoint::Watchpoint(Target &target, lldb::addr_t addr, uint32_t size,
300b57cec5SDimitry Andric const CompilerType *type, bool hardware)
31e8d8bef9SDimitry Andric : StoppointSite(0, addr, size, hardware), m_target(target),
320b57cec5SDimitry Andric m_enabled(false), m_is_hardware(hardware), m_is_watch_variable(false),
330b57cec5SDimitry Andric m_is_ephemeral(false), m_disabled_count(0), m_watch_read(0),
345f757f3fSDimitry Andric m_watch_write(0), m_watch_modify(0), m_ignore_count(0),
355f757f3fSDimitry Andric m_being_created(true) {
369dba64beSDimitry Andric
370b57cec5SDimitry Andric if (type && type->IsValid())
380b57cec5SDimitry Andric m_type = *type;
390b57cec5SDimitry Andric else {
400b57cec5SDimitry Andric // If we don't have a known type, then we force it to unsigned int of the
410b57cec5SDimitry Andric // right size.
429dba64beSDimitry Andric auto type_system_or_err =
439dba64beSDimitry Andric target.GetScratchTypeSystemForLanguage(eLanguageTypeC);
449dba64beSDimitry Andric if (auto err = type_system_or_err.takeError()) {
4581ad6265SDimitry Andric LLDB_LOG_ERROR(GetLog(LLDBLog::Watchpoints), std::move(err),
4606c3fb27SDimitry Andric "Failed to set type: {0}");
479dba64beSDimitry Andric } else {
48bdd1243dSDimitry Andric if (auto ts = *type_system_or_err)
49bdd1243dSDimitry Andric m_type =
50bdd1243dSDimitry Andric ts->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 8 * size);
51bdd1243dSDimitry Andric else
52bdd1243dSDimitry Andric LLDB_LOG_ERROR(GetLog(LLDBLog::Watchpoints), std::move(err),
5306c3fb27SDimitry Andric "Failed to set type: Typesystem is no longer live: {0}");
549dba64beSDimitry Andric }
550b57cec5SDimitry Andric }
560b57cec5SDimitry Andric
570b57cec5SDimitry Andric // Set the initial value of the watched variable:
580b57cec5SDimitry Andric if (m_target.GetProcessSP()) {
590b57cec5SDimitry Andric ExecutionContext exe_ctx;
600b57cec5SDimitry Andric m_target.GetProcessSP()->CalculateExecutionContext(exe_ctx);
610b57cec5SDimitry Andric CaptureWatchedValue(exe_ctx);
620b57cec5SDimitry Andric }
630b57cec5SDimitry Andric m_being_created = false;
640b57cec5SDimitry Andric }
650b57cec5SDimitry Andric
660b57cec5SDimitry Andric Watchpoint::~Watchpoint() = default;
670b57cec5SDimitry Andric
680b57cec5SDimitry Andric // This function is used when "baton" doesn't need to be freed
SetCallback(WatchpointHitCallback callback,void * baton,bool is_synchronous)690b57cec5SDimitry Andric void Watchpoint::SetCallback(WatchpointHitCallback callback, void *baton,
700b57cec5SDimitry Andric bool is_synchronous) {
710b57cec5SDimitry Andric // The default "Baton" class will keep a copy of "baton" and won't free or
725f757f3fSDimitry Andric // delete it when it goes out of scope.
730b57cec5SDimitry Andric m_options.SetCallback(callback, std::make_shared<UntypedBaton>(baton),
740b57cec5SDimitry Andric is_synchronous);
750b57cec5SDimitry Andric
760b57cec5SDimitry Andric SendWatchpointChangedEvent(eWatchpointEventTypeCommandChanged);
770b57cec5SDimitry Andric }
780b57cec5SDimitry Andric
790b57cec5SDimitry Andric // This function is used when a baton needs to be freed and therefore is
800b57cec5SDimitry Andric // contained in a "Baton" subclass.
SetCallback(WatchpointHitCallback callback,const BatonSP & callback_baton_sp,bool is_synchronous)810b57cec5SDimitry Andric void Watchpoint::SetCallback(WatchpointHitCallback callback,
820b57cec5SDimitry Andric const BatonSP &callback_baton_sp,
830b57cec5SDimitry Andric bool is_synchronous) {
840b57cec5SDimitry Andric m_options.SetCallback(callback, callback_baton_sp, is_synchronous);
850b57cec5SDimitry Andric SendWatchpointChangedEvent(eWatchpointEventTypeCommandChanged);
860b57cec5SDimitry Andric }
870b57cec5SDimitry Andric
SetupVariableWatchpointDisabler(StackFrameSP frame_sp) const8806c3fb27SDimitry Andric bool Watchpoint::SetupVariableWatchpointDisabler(StackFrameSP frame_sp) const {
8906c3fb27SDimitry Andric if (!frame_sp)
9006c3fb27SDimitry Andric return false;
9106c3fb27SDimitry Andric
9206c3fb27SDimitry Andric ThreadSP thread_sp = frame_sp->GetThread();
9306c3fb27SDimitry Andric if (!thread_sp)
9406c3fb27SDimitry Andric return false;
9506c3fb27SDimitry Andric
9606c3fb27SDimitry Andric uint32_t return_frame_index =
9706c3fb27SDimitry Andric thread_sp->GetSelectedFrameIndex(DoNoSelectMostRelevantFrame) + 1;
9806c3fb27SDimitry Andric if (return_frame_index >= LLDB_INVALID_FRAME_ID)
9906c3fb27SDimitry Andric return false;
10006c3fb27SDimitry Andric
10106c3fb27SDimitry Andric StackFrameSP return_frame_sp(
10206c3fb27SDimitry Andric thread_sp->GetStackFrameAtIndex(return_frame_index));
10306c3fb27SDimitry Andric if (!return_frame_sp)
10406c3fb27SDimitry Andric return false;
10506c3fb27SDimitry Andric
10606c3fb27SDimitry Andric ExecutionContext exe_ctx(return_frame_sp);
10706c3fb27SDimitry Andric TargetSP target_sp = exe_ctx.GetTargetSP();
10806c3fb27SDimitry Andric if (!target_sp)
10906c3fb27SDimitry Andric return false;
11006c3fb27SDimitry Andric
11106c3fb27SDimitry Andric Address return_address(return_frame_sp->GetFrameCodeAddress());
11206c3fb27SDimitry Andric lldb::addr_t return_addr = return_address.GetLoadAddress(target_sp.get());
11306c3fb27SDimitry Andric if (return_addr == LLDB_INVALID_ADDRESS)
11406c3fb27SDimitry Andric return false;
11506c3fb27SDimitry Andric
11606c3fb27SDimitry Andric BreakpointSP bp_sp = target_sp->CreateBreakpoint(
11706c3fb27SDimitry Andric return_addr, /*internal=*/true, /*request_hardware=*/false);
11806c3fb27SDimitry Andric if (!bp_sp || !bp_sp->HasResolvedLocations())
11906c3fb27SDimitry Andric return false;
12006c3fb27SDimitry Andric
12106c3fb27SDimitry Andric auto wvc_up = std::make_unique<WatchpointVariableContext>(GetID(), exe_ctx);
12206c3fb27SDimitry Andric auto baton_sp = std::make_shared<WatchpointVariableBaton>(std::move(wvc_up));
12306c3fb27SDimitry Andric bp_sp->SetCallback(VariableWatchpointDisabler, baton_sp);
12406c3fb27SDimitry Andric bp_sp->SetOneShot(true);
12506c3fb27SDimitry Andric bp_sp->SetBreakpointKind("variable watchpoint disabler");
12606c3fb27SDimitry Andric return true;
12706c3fb27SDimitry Andric }
12806c3fb27SDimitry Andric
VariableWatchpointDisabler(void * baton,StoppointCallbackContext * context,user_id_t break_id,user_id_t break_loc_id)12906c3fb27SDimitry Andric bool Watchpoint::VariableWatchpointDisabler(void *baton,
13006c3fb27SDimitry Andric StoppointCallbackContext *context,
13106c3fb27SDimitry Andric user_id_t break_id,
13206c3fb27SDimitry Andric user_id_t break_loc_id) {
13306c3fb27SDimitry Andric assert(baton && "null baton");
13406c3fb27SDimitry Andric if (!baton || !context)
13506c3fb27SDimitry Andric return false;
13606c3fb27SDimitry Andric
13706c3fb27SDimitry Andric Log *log = GetLog(LLDBLog::Watchpoints);
13806c3fb27SDimitry Andric
13906c3fb27SDimitry Andric WatchpointVariableContext *wvc =
14006c3fb27SDimitry Andric static_cast<WatchpointVariableContext *>(baton);
14106c3fb27SDimitry Andric
14206c3fb27SDimitry Andric LLDB_LOGF(log, "called by breakpoint %" PRIu64 ".%" PRIu64, break_id,
14306c3fb27SDimitry Andric break_loc_id);
14406c3fb27SDimitry Andric
14506c3fb27SDimitry Andric if (wvc->watch_id == LLDB_INVALID_WATCH_ID)
14606c3fb27SDimitry Andric return false;
14706c3fb27SDimitry Andric
14806c3fb27SDimitry Andric TargetSP target_sp = context->exe_ctx_ref.GetTargetSP();
14906c3fb27SDimitry Andric if (!target_sp)
15006c3fb27SDimitry Andric return false;
15106c3fb27SDimitry Andric
15206c3fb27SDimitry Andric ProcessSP process_sp = target_sp->GetProcessSP();
15306c3fb27SDimitry Andric if (!process_sp)
15406c3fb27SDimitry Andric return false;
15506c3fb27SDimitry Andric
15606c3fb27SDimitry Andric WatchpointSP watch_sp =
15706c3fb27SDimitry Andric target_sp->GetWatchpointList().FindByID(wvc->watch_id);
15806c3fb27SDimitry Andric if (!watch_sp)
15906c3fb27SDimitry Andric return false;
16006c3fb27SDimitry Andric
16106c3fb27SDimitry Andric if (wvc->exe_ctx == context->exe_ctx_ref) {
16206c3fb27SDimitry Andric LLDB_LOGF(log,
16306c3fb27SDimitry Andric "callback for watchpoint %" PRId32
16406c3fb27SDimitry Andric " matched internal breakpoint execution context",
16506c3fb27SDimitry Andric watch_sp->GetID());
1665f757f3fSDimitry Andric process_sp->DisableWatchpoint(watch_sp);
16706c3fb27SDimitry Andric return false;
16806c3fb27SDimitry Andric }
16906c3fb27SDimitry Andric LLDB_LOGF(log,
17006c3fb27SDimitry Andric "callback for watchpoint %" PRId32
17106c3fb27SDimitry Andric " didn't match internal breakpoint execution context",
17206c3fb27SDimitry Andric watch_sp->GetID());
17306c3fb27SDimitry Andric return false;
17406c3fb27SDimitry Andric }
17506c3fb27SDimitry Andric
ClearCallback()1760b57cec5SDimitry Andric void Watchpoint::ClearCallback() {
1770b57cec5SDimitry Andric m_options.ClearCallback();
1780b57cec5SDimitry Andric SendWatchpointChangedEvent(eWatchpointEventTypeCommandChanged);
1790b57cec5SDimitry Andric }
1800b57cec5SDimitry Andric
SetDeclInfo(const std::string & str)1810b57cec5SDimitry Andric void Watchpoint::SetDeclInfo(const std::string &str) { m_decl_str = str; }
1820b57cec5SDimitry Andric
GetWatchSpec()1830b57cec5SDimitry Andric std::string Watchpoint::GetWatchSpec() { return m_watch_spec_str; }
1840b57cec5SDimitry Andric
SetWatchSpec(const std::string & str)1850b57cec5SDimitry Andric void Watchpoint::SetWatchSpec(const std::string &str) {
1860b57cec5SDimitry Andric m_watch_spec_str = str;
1870b57cec5SDimitry Andric }
1880b57cec5SDimitry Andric
IsHardware() const189e8d8bef9SDimitry Andric bool Watchpoint::IsHardware() const {
190e8d8bef9SDimitry Andric lldbassert(m_is_hardware || !HardwareRequired());
191e8d8bef9SDimitry Andric return m_is_hardware;
192e8d8bef9SDimitry Andric }
1930b57cec5SDimitry Andric
IsWatchVariable() const1940b57cec5SDimitry Andric bool Watchpoint::IsWatchVariable() const { return m_is_watch_variable; }
1950b57cec5SDimitry Andric
SetWatchVariable(bool val)1960b57cec5SDimitry Andric void Watchpoint::SetWatchVariable(bool val) { m_is_watch_variable = val; }
1970b57cec5SDimitry Andric
CaptureWatchedValue(const ExecutionContext & exe_ctx)1980b57cec5SDimitry Andric bool Watchpoint::CaptureWatchedValue(const ExecutionContext &exe_ctx) {
1995f757f3fSDimitry Andric ConstString g_watch_name("$__lldb__watch_value");
2000b57cec5SDimitry Andric m_old_value_sp = m_new_value_sp;
2010b57cec5SDimitry Andric Address watch_address(GetLoadAddress());
2020b57cec5SDimitry Andric if (!m_type.IsValid()) {
2030b57cec5SDimitry Andric // Don't know how to report new & old values, since we couldn't make a
2040b57cec5SDimitry Andric // scalar type for this watchpoint. This works around an assert in
2050b57cec5SDimitry Andric // ValueObjectMemory::Create.
2060b57cec5SDimitry Andric // FIXME: This should not happen, but if it does in some case we care about,
2070b57cec5SDimitry Andric // we can go grab the value raw and print it as unsigned.
2080b57cec5SDimitry Andric return false;
2090b57cec5SDimitry Andric }
2100b57cec5SDimitry Andric m_new_value_sp = ValueObjectMemory::Create(
2115f757f3fSDimitry Andric exe_ctx.GetBestExecutionContextScope(), g_watch_name.GetStringRef(),
2120b57cec5SDimitry Andric watch_address, m_type);
2135f757f3fSDimitry Andric m_new_value_sp = m_new_value_sp->CreateConstantValue(g_watch_name);
2140b57cec5SDimitry Andric return (m_new_value_sp && m_new_value_sp->GetError().Success());
2150b57cec5SDimitry Andric }
2160b57cec5SDimitry Andric
WatchedValueReportable(const ExecutionContext & exe_ctx)2175f757f3fSDimitry Andric bool Watchpoint::WatchedValueReportable(const ExecutionContext &exe_ctx) {
2185f757f3fSDimitry Andric if (!m_watch_modify || m_watch_read)
2195f757f3fSDimitry Andric return true;
2205f757f3fSDimitry Andric if (!m_type.IsValid())
2215f757f3fSDimitry Andric return true;
2225f757f3fSDimitry Andric
2235f757f3fSDimitry Andric ConstString g_watch_name("$__lldb__watch_value");
2245f757f3fSDimitry Andric Address watch_address(GetLoadAddress());
2255f757f3fSDimitry Andric ValueObjectSP newest_valueobj_sp = ValueObjectMemory::Create(
2265f757f3fSDimitry Andric exe_ctx.GetBestExecutionContextScope(), g_watch_name.GetStringRef(),
2275f757f3fSDimitry Andric watch_address, m_type);
2285f757f3fSDimitry Andric newest_valueobj_sp = newest_valueobj_sp->CreateConstantValue(g_watch_name);
2295f757f3fSDimitry Andric Status error;
2305f757f3fSDimitry Andric
2315f757f3fSDimitry Andric DataExtractor new_data;
2325f757f3fSDimitry Andric DataExtractor old_data;
2335f757f3fSDimitry Andric
2345f757f3fSDimitry Andric newest_valueobj_sp->GetData(new_data, error);
2355f757f3fSDimitry Andric if (error.Fail())
2365f757f3fSDimitry Andric return true;
2375f757f3fSDimitry Andric m_new_value_sp->GetData(old_data, error);
2385f757f3fSDimitry Andric if (error.Fail())
2395f757f3fSDimitry Andric return true;
2405f757f3fSDimitry Andric
2415f757f3fSDimitry Andric if (new_data.GetByteSize() != old_data.GetByteSize() ||
2425f757f3fSDimitry Andric new_data.GetByteSize() == 0)
2435f757f3fSDimitry Andric return true;
2445f757f3fSDimitry Andric
2455f757f3fSDimitry Andric if (memcmp(new_data.GetDataStart(), old_data.GetDataStart(),
2465f757f3fSDimitry Andric old_data.GetByteSize()) == 0)
2475f757f3fSDimitry Andric return false; // Value has not changed, user requested modify watchpoint
2485f757f3fSDimitry Andric
2495f757f3fSDimitry Andric return true;
2505f757f3fSDimitry Andric }
2515f757f3fSDimitry Andric
2520b57cec5SDimitry Andric // RETURNS - true if we should stop at this breakpoint, false if we
2530b57cec5SDimitry Andric // should continue.
2540b57cec5SDimitry Andric
ShouldStop(StoppointCallbackContext * context)2550b57cec5SDimitry Andric bool Watchpoint::ShouldStop(StoppointCallbackContext *context) {
256e8d8bef9SDimitry Andric m_hit_counter.Increment();
2570b57cec5SDimitry Andric
2580b57cec5SDimitry Andric return IsEnabled();
2590b57cec5SDimitry Andric }
2600b57cec5SDimitry Andric
GetDescription(Stream * s,lldb::DescriptionLevel level)2610b57cec5SDimitry Andric void Watchpoint::GetDescription(Stream *s, lldb::DescriptionLevel level) {
2620b57cec5SDimitry Andric DumpWithLevel(s, level);
2630b57cec5SDimitry Andric }
2640b57cec5SDimitry Andric
Dump(Stream * s) const2650b57cec5SDimitry Andric void Watchpoint::Dump(Stream *s) const {
2660b57cec5SDimitry Andric DumpWithLevel(s, lldb::eDescriptionLevelBrief);
2670b57cec5SDimitry Andric }
2680b57cec5SDimitry Andric
2690b57cec5SDimitry Andric // If prefix is nullptr, we display the watch id and ignore the prefix
2700b57cec5SDimitry Andric // altogether.
DumpSnapshots(Stream * s,const char * prefix) const2715f757f3fSDimitry Andric bool Watchpoint::DumpSnapshots(Stream *s, const char *prefix) const {
2725f757f3fSDimitry Andric bool printed_anything = false;
2735f757f3fSDimitry Andric
2745f757f3fSDimitry Andric // For read watchpoints, don't display any before/after value changes.
2755f757f3fSDimitry Andric if (m_watch_read && !m_watch_modify && !m_watch_write)
2765f757f3fSDimitry Andric return printed_anything;
2775f757f3fSDimitry Andric
2785f757f3fSDimitry Andric s->Printf("\n");
2795f757f3fSDimitry Andric s->Printf("Watchpoint %u hit:\n", GetID());
2805f757f3fSDimitry Andric
2815f757f3fSDimitry Andric StreamString values_ss;
2825f757f3fSDimitry Andric if (prefix)
2835f757f3fSDimitry Andric values_ss.Indent(prefix);
2840b57cec5SDimitry Andric
2850b57cec5SDimitry Andric if (m_old_value_sp) {
2865f757f3fSDimitry Andric if (auto *old_value_cstr = m_old_value_sp->GetValueAsCString()) {
2875f757f3fSDimitry Andric values_ss.Printf("old value: %s", old_value_cstr);
2885f757f3fSDimitry Andric } else {
2895f757f3fSDimitry Andric if (auto *old_summary_cstr = m_old_value_sp->GetSummaryAsCString())
2905f757f3fSDimitry Andric values_ss.Printf("old value: %s", old_summary_cstr);
2910b57cec5SDimitry Andric else {
2925f757f3fSDimitry Andric StreamString strm;
2935f757f3fSDimitry Andric DumpValueObjectOptions options;
2945f757f3fSDimitry Andric options.SetUseDynamicType(eNoDynamicValues)
2955f757f3fSDimitry Andric .SetHideRootType(true)
2965f757f3fSDimitry Andric .SetHideRootName(true)
2975f757f3fSDimitry Andric .SetHideName(true);
2985f757f3fSDimitry Andric m_old_value_sp->Dump(strm, options);
2995f757f3fSDimitry Andric if (strm.GetData())
3005f757f3fSDimitry Andric values_ss.Printf("old value: %s", strm.GetData());
3015f757f3fSDimitry Andric }
3020b57cec5SDimitry Andric }
3030b57cec5SDimitry Andric }
3040b57cec5SDimitry Andric
3050b57cec5SDimitry Andric if (m_new_value_sp) {
3065f757f3fSDimitry Andric if (values_ss.GetSize())
3075f757f3fSDimitry Andric values_ss.Printf("\n");
3085f757f3fSDimitry Andric
3095f757f3fSDimitry Andric if (auto *new_value_cstr = m_new_value_sp->GetValueAsCString())
3105f757f3fSDimitry Andric values_ss.Printf("new value: %s", new_value_cstr);
3110b57cec5SDimitry Andric else {
3125f757f3fSDimitry Andric if (auto *new_summary_cstr = m_new_value_sp->GetSummaryAsCString())
3135f757f3fSDimitry Andric values_ss.Printf("new value: %s", new_summary_cstr);
3145f757f3fSDimitry Andric else {
3155f757f3fSDimitry Andric StreamString strm;
3165f757f3fSDimitry Andric DumpValueObjectOptions options;
3175f757f3fSDimitry Andric options.SetUseDynamicType(eNoDynamicValues)
3185f757f3fSDimitry Andric .SetHideRootType(true)
3195f757f3fSDimitry Andric .SetHideRootName(true)
3205f757f3fSDimitry Andric .SetHideName(true);
3215f757f3fSDimitry Andric m_new_value_sp->Dump(strm, options);
3225f757f3fSDimitry Andric if (strm.GetData())
3235f757f3fSDimitry Andric values_ss.Printf("new value: %s", strm.GetData());
3240b57cec5SDimitry Andric }
3250b57cec5SDimitry Andric }
3260b57cec5SDimitry Andric }
3270b57cec5SDimitry Andric
3285f757f3fSDimitry Andric if (values_ss.GetSize()) {
3295f757f3fSDimitry Andric s->Printf("%s", values_ss.GetData());
3305f757f3fSDimitry Andric printed_anything = true;
3315f757f3fSDimitry Andric }
3325f757f3fSDimitry Andric
3335f757f3fSDimitry Andric return printed_anything;
3345f757f3fSDimitry Andric }
3355f757f3fSDimitry Andric
DumpWithLevel(Stream * s,lldb::DescriptionLevel description_level) const3360b57cec5SDimitry Andric void Watchpoint::DumpWithLevel(Stream *s,
3370b57cec5SDimitry Andric lldb::DescriptionLevel description_level) const {
3380b57cec5SDimitry Andric if (s == nullptr)
3390b57cec5SDimitry Andric return;
3400b57cec5SDimitry Andric
3410b57cec5SDimitry Andric assert(description_level >= lldb::eDescriptionLevelBrief &&
3420b57cec5SDimitry Andric description_level <= lldb::eDescriptionLevelVerbose);
3430b57cec5SDimitry Andric
3440b57cec5SDimitry Andric s->Printf("Watchpoint %u: addr = 0x%8.8" PRIx64
3455f757f3fSDimitry Andric " size = %u state = %s type = %s%s%s",
3460b57cec5SDimitry Andric GetID(), GetLoadAddress(), m_byte_size,
3470b57cec5SDimitry Andric IsEnabled() ? "enabled" : "disabled", m_watch_read ? "r" : "",
3485f757f3fSDimitry Andric m_watch_write ? "w" : "", m_watch_modify ? "m" : "");
3490b57cec5SDimitry Andric
3500b57cec5SDimitry Andric if (description_level >= lldb::eDescriptionLevelFull) {
3510b57cec5SDimitry Andric if (!m_decl_str.empty())
3520b57cec5SDimitry Andric s->Printf("\n declare @ '%s'", m_decl_str.c_str());
3530b57cec5SDimitry Andric if (!m_watch_spec_str.empty())
3540b57cec5SDimitry Andric s->Printf("\n watchpoint spec = '%s'", m_watch_spec_str.c_str());
3550b57cec5SDimitry Andric
3560b57cec5SDimitry Andric // Dump the snapshots we have taken.
3570b57cec5SDimitry Andric DumpSnapshots(s, " ");
3580b57cec5SDimitry Andric
3590b57cec5SDimitry Andric if (GetConditionText())
3600b57cec5SDimitry Andric s->Printf("\n condition = '%s'", GetConditionText());
3610b57cec5SDimitry Andric m_options.GetCallbackDescription(s, description_level);
3620b57cec5SDimitry Andric }
3630b57cec5SDimitry Andric
3640b57cec5SDimitry Andric if (description_level >= lldb::eDescriptionLevelVerbose) {
3655f757f3fSDimitry Andric s->Printf("\n hit_count = %-4u ignore_count = %-4u", GetHitCount(),
3665f757f3fSDimitry Andric GetIgnoreCount());
3670b57cec5SDimitry Andric }
3680b57cec5SDimitry Andric }
3690b57cec5SDimitry Andric
IsEnabled() const3700b57cec5SDimitry Andric bool Watchpoint::IsEnabled() const { return m_enabled; }
3710b57cec5SDimitry Andric
3720b57cec5SDimitry Andric // Within StopInfo.cpp, we purposely turn on the ephemeral mode right before
3730b57cec5SDimitry Andric // temporarily disable the watchpoint in order to perform possible watchpoint
3740b57cec5SDimitry Andric // actions without triggering further watchpoint events. After the temporary
3750b57cec5SDimitry Andric // disabled watchpoint is enabled, we then turn off the ephemeral mode.
3760b57cec5SDimitry Andric
TurnOnEphemeralMode()3770b57cec5SDimitry Andric void Watchpoint::TurnOnEphemeralMode() { m_is_ephemeral = true; }
3780b57cec5SDimitry Andric
TurnOffEphemeralMode()3790b57cec5SDimitry Andric void Watchpoint::TurnOffEphemeralMode() {
3800b57cec5SDimitry Andric m_is_ephemeral = false;
3810b57cec5SDimitry Andric // Leaving ephemeral mode, reset the m_disabled_count!
3820b57cec5SDimitry Andric m_disabled_count = 0;
3830b57cec5SDimitry Andric }
3840b57cec5SDimitry Andric
IsDisabledDuringEphemeralMode()3850b57cec5SDimitry Andric bool Watchpoint::IsDisabledDuringEphemeralMode() {
3860b57cec5SDimitry Andric return m_disabled_count > 1 && m_is_ephemeral;
3870b57cec5SDimitry Andric }
3880b57cec5SDimitry Andric
SetEnabled(bool enabled,bool notify)3890b57cec5SDimitry Andric void Watchpoint::SetEnabled(bool enabled, bool notify) {
3900b57cec5SDimitry Andric if (!enabled) {
3915f757f3fSDimitry Andric if (m_is_ephemeral)
3920b57cec5SDimitry Andric ++m_disabled_count;
3930b57cec5SDimitry Andric
3940b57cec5SDimitry Andric // Don't clear the snapshots for now.
3950b57cec5SDimitry Andric // Within StopInfo.cpp, we purposely do disable/enable watchpoint while
3960b57cec5SDimitry Andric // performing watchpoint actions.
3970b57cec5SDimitry Andric }
3980b57cec5SDimitry Andric bool changed = enabled != m_enabled;
3990b57cec5SDimitry Andric m_enabled = enabled;
4000b57cec5SDimitry Andric if (notify && !m_is_ephemeral && changed)
4010b57cec5SDimitry Andric SendWatchpointChangedEvent(enabled ? eWatchpointEventTypeEnabled
4020b57cec5SDimitry Andric : eWatchpointEventTypeDisabled);
4030b57cec5SDimitry Andric }
4040b57cec5SDimitry Andric
SetWatchpointType(uint32_t type,bool notify)4050b57cec5SDimitry Andric void Watchpoint::SetWatchpointType(uint32_t type, bool notify) {
4060b57cec5SDimitry Andric int old_watch_read = m_watch_read;
4070b57cec5SDimitry Andric int old_watch_write = m_watch_write;
4085f757f3fSDimitry Andric int old_watch_modify = m_watch_modify;
4090b57cec5SDimitry Andric m_watch_read = (type & LLDB_WATCH_TYPE_READ) != 0;
4100b57cec5SDimitry Andric m_watch_write = (type & LLDB_WATCH_TYPE_WRITE) != 0;
4115f757f3fSDimitry Andric m_watch_modify = (type & LLDB_WATCH_TYPE_MODIFY) != 0;
4120b57cec5SDimitry Andric if (notify &&
4135f757f3fSDimitry Andric (old_watch_read != m_watch_read || old_watch_write != m_watch_write ||
4145f757f3fSDimitry Andric old_watch_modify != m_watch_modify))
4150b57cec5SDimitry Andric SendWatchpointChangedEvent(eWatchpointEventTypeTypeChanged);
4160b57cec5SDimitry Andric }
4170b57cec5SDimitry Andric
WatchpointRead() const4180b57cec5SDimitry Andric bool Watchpoint::WatchpointRead() const { return m_watch_read != 0; }
4190b57cec5SDimitry Andric
WatchpointWrite() const4200b57cec5SDimitry Andric bool Watchpoint::WatchpointWrite() const { return m_watch_write != 0; }
4210b57cec5SDimitry Andric
WatchpointModify() const4225f757f3fSDimitry Andric bool Watchpoint::WatchpointModify() const { return m_watch_modify != 0; }
4235f757f3fSDimitry Andric
GetIgnoreCount() const4240b57cec5SDimitry Andric uint32_t Watchpoint::GetIgnoreCount() const { return m_ignore_count; }
4250b57cec5SDimitry Andric
SetIgnoreCount(uint32_t n)4260b57cec5SDimitry Andric void Watchpoint::SetIgnoreCount(uint32_t n) {
4270b57cec5SDimitry Andric bool changed = m_ignore_count != n;
4280b57cec5SDimitry Andric m_ignore_count = n;
4290b57cec5SDimitry Andric if (changed)
4300b57cec5SDimitry Andric SendWatchpointChangedEvent(eWatchpointEventTypeIgnoreChanged);
4310b57cec5SDimitry Andric }
4320b57cec5SDimitry Andric
InvokeCallback(StoppointCallbackContext * context)4330b57cec5SDimitry Andric bool Watchpoint::InvokeCallback(StoppointCallbackContext *context) {
4340b57cec5SDimitry Andric return m_options.InvokeCallback(context, GetID());
4350b57cec5SDimitry Andric }
4360b57cec5SDimitry Andric
SetCondition(const char * condition)4370b57cec5SDimitry Andric void Watchpoint::SetCondition(const char *condition) {
4380b57cec5SDimitry Andric if (condition == nullptr || condition[0] == '\0') {
4390b57cec5SDimitry Andric if (m_condition_up)
4400b57cec5SDimitry Andric m_condition_up.reset();
4410b57cec5SDimitry Andric } else {
4420b57cec5SDimitry Andric // Pass nullptr for expr_prefix (no translation-unit level definitions).
4430b57cec5SDimitry Andric Status error;
4440b57cec5SDimitry Andric m_condition_up.reset(m_target.GetUserExpressionForLanguage(
4450b57cec5SDimitry Andric condition, llvm::StringRef(), lldb::eLanguageTypeUnknown,
4460b57cec5SDimitry Andric UserExpression::eResultTypeAny, EvaluateExpressionOptions(), nullptr,
4470b57cec5SDimitry Andric error));
4480b57cec5SDimitry Andric if (error.Fail()) {
4490b57cec5SDimitry Andric // FIXME: Log something...
4500b57cec5SDimitry Andric m_condition_up.reset();
4510b57cec5SDimitry Andric }
4520b57cec5SDimitry Andric }
4530b57cec5SDimitry Andric SendWatchpointChangedEvent(eWatchpointEventTypeConditionChanged);
4540b57cec5SDimitry Andric }
4550b57cec5SDimitry Andric
GetConditionText() const4560b57cec5SDimitry Andric const char *Watchpoint::GetConditionText() const {
4570b57cec5SDimitry Andric if (m_condition_up)
4580b57cec5SDimitry Andric return m_condition_up->GetUserText();
4590b57cec5SDimitry Andric else
4600b57cec5SDimitry Andric return nullptr;
4610b57cec5SDimitry Andric }
4620b57cec5SDimitry Andric
SendWatchpointChangedEvent(lldb::WatchpointEventType eventKind)4630b57cec5SDimitry Andric void Watchpoint::SendWatchpointChangedEvent(
4640b57cec5SDimitry Andric lldb::WatchpointEventType eventKind) {
4657a6dacacSDimitry Andric if (!m_being_created && GetTarget().EventTypeHasListeners(
4660b57cec5SDimitry Andric Target::eBroadcastBitWatchpointChanged)) {
4677a6dacacSDimitry Andric auto data_sp =
4687a6dacacSDimitry Andric std::make_shared<WatchpointEventData>(eventKind, shared_from_this());
4697a6dacacSDimitry Andric GetTarget().BroadcastEvent(Target::eBroadcastBitWatchpointChanged, data_sp);
4700b57cec5SDimitry Andric }
4710b57cec5SDimitry Andric }
4720b57cec5SDimitry Andric
WatchpointEventData(WatchpointEventType sub_type,const WatchpointSP & new_watchpoint_sp)4730b57cec5SDimitry Andric Watchpoint::WatchpointEventData::WatchpointEventData(
4740b57cec5SDimitry Andric WatchpointEventType sub_type, const WatchpointSP &new_watchpoint_sp)
47581ad6265SDimitry Andric : m_watchpoint_event(sub_type), m_new_watchpoint_sp(new_watchpoint_sp) {}
4760b57cec5SDimitry Andric
4770b57cec5SDimitry Andric Watchpoint::WatchpointEventData::~WatchpointEventData() = default;
4780b57cec5SDimitry Andric
GetFlavorString()47906c3fb27SDimitry Andric llvm::StringRef Watchpoint::WatchpointEventData::GetFlavorString() {
48006c3fb27SDimitry Andric return "Watchpoint::WatchpointEventData";
4810b57cec5SDimitry Andric }
4820b57cec5SDimitry Andric
GetFlavor() const48306c3fb27SDimitry Andric llvm::StringRef Watchpoint::WatchpointEventData::GetFlavor() const {
4840b57cec5SDimitry Andric return WatchpointEventData::GetFlavorString();
4850b57cec5SDimitry Andric }
4860b57cec5SDimitry Andric
GetWatchpoint()4870b57cec5SDimitry Andric WatchpointSP &Watchpoint::WatchpointEventData::GetWatchpoint() {
4880b57cec5SDimitry Andric return m_new_watchpoint_sp;
4890b57cec5SDimitry Andric }
4900b57cec5SDimitry Andric
4910b57cec5SDimitry Andric WatchpointEventType
GetWatchpointEventType() const4920b57cec5SDimitry Andric Watchpoint::WatchpointEventData::GetWatchpointEventType() const {
4930b57cec5SDimitry Andric return m_watchpoint_event;
4940b57cec5SDimitry Andric }
4950b57cec5SDimitry Andric
Dump(Stream * s) const4960b57cec5SDimitry Andric void Watchpoint::WatchpointEventData::Dump(Stream *s) const {}
4970b57cec5SDimitry Andric
4980b57cec5SDimitry Andric const Watchpoint::WatchpointEventData *
GetEventDataFromEvent(const Event * event)4990b57cec5SDimitry Andric Watchpoint::WatchpointEventData::GetEventDataFromEvent(const Event *event) {
5000b57cec5SDimitry Andric if (event) {
5010b57cec5SDimitry Andric const EventData *event_data = event->GetData();
5020b57cec5SDimitry Andric if (event_data &&
5030b57cec5SDimitry Andric event_data->GetFlavor() == WatchpointEventData::GetFlavorString())
5040b57cec5SDimitry Andric return static_cast<const WatchpointEventData *>(event->GetData());
5050b57cec5SDimitry Andric }
5060b57cec5SDimitry Andric return nullptr;
5070b57cec5SDimitry Andric }
5080b57cec5SDimitry Andric
5090b57cec5SDimitry Andric WatchpointEventType
GetWatchpointEventTypeFromEvent(const EventSP & event_sp)5100b57cec5SDimitry Andric Watchpoint::WatchpointEventData::GetWatchpointEventTypeFromEvent(
5110b57cec5SDimitry Andric const EventSP &event_sp) {
5120b57cec5SDimitry Andric const WatchpointEventData *data = GetEventDataFromEvent(event_sp.get());
5130b57cec5SDimitry Andric
5140b57cec5SDimitry Andric if (data == nullptr)
5150b57cec5SDimitry Andric return eWatchpointEventTypeInvalidType;
5160b57cec5SDimitry Andric else
5170b57cec5SDimitry Andric return data->GetWatchpointEventType();
5180b57cec5SDimitry Andric }
5190b57cec5SDimitry Andric
GetWatchpointFromEvent(const EventSP & event_sp)5200b57cec5SDimitry Andric WatchpointSP Watchpoint::WatchpointEventData::GetWatchpointFromEvent(
5210b57cec5SDimitry Andric const EventSP &event_sp) {
5220b57cec5SDimitry Andric WatchpointSP wp_sp;
5230b57cec5SDimitry Andric
5240b57cec5SDimitry Andric const WatchpointEventData *data = GetEventDataFromEvent(event_sp.get());
5250b57cec5SDimitry Andric if (data)
5260b57cec5SDimitry Andric wp_sp = data->m_new_watchpoint_sp;
5270b57cec5SDimitry Andric
5280b57cec5SDimitry Andric return wp_sp;
5290b57cec5SDimitry Andric }
530