Path: blob/main/contrib/llvm-project/lldb/source/Breakpoint/Watchpoint.cpp
39587 views
//===-- Watchpoint.cpp ----------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "lldb/Breakpoint/Watchpoint.h"910#include "lldb/Breakpoint/StoppointCallbackContext.h"11#include "lldb/Breakpoint/WatchpointResource.h"12#include "lldb/Core/Value.h"13#include "lldb/Core/ValueObject.h"14#include "lldb/Core/ValueObjectMemory.h"15#include "lldb/DataFormatters/DumpValueObjectOptions.h"16#include "lldb/Expression/UserExpression.h"17#include "lldb/Symbol/TypeSystem.h"18#include "lldb/Target/Process.h"19#include "lldb/Target/Target.h"20#include "lldb/Target/ThreadSpec.h"21#include "lldb/Utility/LLDBLog.h"22#include "lldb/Utility/Log.h"23#include "lldb/Utility/Stream.h"2425using namespace lldb;26using namespace lldb_private;2728Watchpoint::Watchpoint(Target &target, lldb::addr_t addr, uint32_t size,29const CompilerType *type, bool hardware)30: StoppointSite(0, addr, size, hardware), m_target(target),31m_enabled(false), m_is_hardware(hardware), m_is_watch_variable(false),32m_is_ephemeral(false), m_disabled_count(0), m_watch_read(0),33m_watch_write(0), m_watch_modify(0), m_ignore_count(0) {3435if (type && type->IsValid())36m_type = *type;37else {38// If we don't have a known type, then we force it to unsigned int of the39// right size.40auto type_system_or_err =41target.GetScratchTypeSystemForLanguage(eLanguageTypeC);42if (auto err = type_system_or_err.takeError()) {43LLDB_LOG_ERROR(GetLog(LLDBLog::Watchpoints), std::move(err),44"Failed to set type: {0}");45} else {46if (auto ts = *type_system_or_err) {47if (size <= target.GetArchitecture().GetAddressByteSize()) {48m_type =49ts->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 8 * size);50} else {51CompilerType clang_uint8_type =52ts->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 8);53m_type = clang_uint8_type.GetArrayType(size);54}55} else56LLDB_LOG_ERROR(GetLog(LLDBLog::Watchpoints), std::move(err),57"Failed to set type: Typesystem is no longer live: {0}");58}59}6061// Set the initial value of the watched variable:62if (m_target.GetProcessSP()) {63ExecutionContext exe_ctx;64m_target.GetProcessSP()->CalculateExecutionContext(exe_ctx);65CaptureWatchedValue(exe_ctx);66}67}6869Watchpoint::~Watchpoint() = default;7071// This function is used when "baton" doesn't need to be freed72void Watchpoint::SetCallback(WatchpointHitCallback callback, void *baton,73bool is_synchronous) {74// The default "Baton" class will keep a copy of "baton" and won't free or75// delete it when it goes out of scope.76m_options.SetCallback(callback, std::make_shared<UntypedBaton>(baton),77is_synchronous);7879SendWatchpointChangedEvent(eWatchpointEventTypeCommandChanged);80}8182// This function is used when a baton needs to be freed and therefore is83// contained in a "Baton" subclass.84void Watchpoint::SetCallback(WatchpointHitCallback callback,85const BatonSP &callback_baton_sp,86bool is_synchronous) {87m_options.SetCallback(callback, callback_baton_sp, is_synchronous);88SendWatchpointChangedEvent(eWatchpointEventTypeCommandChanged);89}9091bool Watchpoint::SetupVariableWatchpointDisabler(StackFrameSP frame_sp) const {92if (!frame_sp)93return false;9495ThreadSP thread_sp = frame_sp->GetThread();96if (!thread_sp)97return false;9899uint32_t return_frame_index =100thread_sp->GetSelectedFrameIndex(DoNoSelectMostRelevantFrame) + 1;101if (return_frame_index >= LLDB_INVALID_FRAME_ID)102return false;103104StackFrameSP return_frame_sp(105thread_sp->GetStackFrameAtIndex(return_frame_index));106if (!return_frame_sp)107return false;108109ExecutionContext exe_ctx(return_frame_sp);110TargetSP target_sp = exe_ctx.GetTargetSP();111if (!target_sp)112return false;113114Address return_address(return_frame_sp->GetFrameCodeAddress());115lldb::addr_t return_addr = return_address.GetLoadAddress(target_sp.get());116if (return_addr == LLDB_INVALID_ADDRESS)117return false;118119BreakpointSP bp_sp = target_sp->CreateBreakpoint(120return_addr, /*internal=*/true, /*request_hardware=*/false);121if (!bp_sp || !bp_sp->HasResolvedLocations())122return false;123124auto wvc_up = std::make_unique<WatchpointVariableContext>(GetID(), exe_ctx);125auto baton_sp = std::make_shared<WatchpointVariableBaton>(std::move(wvc_up));126bp_sp->SetCallback(VariableWatchpointDisabler, baton_sp);127bp_sp->SetOneShot(true);128bp_sp->SetBreakpointKind("variable watchpoint disabler");129return true;130}131132bool Watchpoint::VariableWatchpointDisabler(void *baton,133StoppointCallbackContext *context,134user_id_t break_id,135user_id_t break_loc_id) {136assert(baton && "null baton");137if (!baton || !context)138return false;139140Log *log = GetLog(LLDBLog::Watchpoints);141142WatchpointVariableContext *wvc =143static_cast<WatchpointVariableContext *>(baton);144145LLDB_LOGF(log, "called by breakpoint %" PRIu64 ".%" PRIu64, break_id,146break_loc_id);147148if (wvc->watch_id == LLDB_INVALID_WATCH_ID)149return false;150151TargetSP target_sp = context->exe_ctx_ref.GetTargetSP();152if (!target_sp)153return false;154155ProcessSP process_sp = target_sp->GetProcessSP();156if (!process_sp)157return false;158159WatchpointSP watch_sp =160target_sp->GetWatchpointList().FindByID(wvc->watch_id);161if (!watch_sp)162return false;163164if (wvc->exe_ctx == context->exe_ctx_ref) {165LLDB_LOGF(log,166"callback for watchpoint %" PRId32167" matched internal breakpoint execution context",168watch_sp->GetID());169process_sp->DisableWatchpoint(watch_sp);170return false;171}172LLDB_LOGF(log,173"callback for watchpoint %" PRId32174" didn't match internal breakpoint execution context",175watch_sp->GetID());176return false;177}178179void Watchpoint::ClearCallback() {180m_options.ClearCallback();181SendWatchpointChangedEvent(eWatchpointEventTypeCommandChanged);182}183184void Watchpoint::SetDeclInfo(const std::string &str) { m_decl_str = str; }185186std::string Watchpoint::GetWatchSpec() { return m_watch_spec_str; }187188void Watchpoint::SetWatchSpec(const std::string &str) {189m_watch_spec_str = str;190}191192bool Watchpoint::IsHardware() const {193lldbassert(m_is_hardware || !HardwareRequired());194return m_is_hardware;195}196197bool Watchpoint::IsWatchVariable() const { return m_is_watch_variable; }198199void Watchpoint::SetWatchVariable(bool val) { m_is_watch_variable = val; }200201bool Watchpoint::CaptureWatchedValue(const ExecutionContext &exe_ctx) {202ConstString g_watch_name("$__lldb__watch_value");203m_old_value_sp = m_new_value_sp;204Address watch_address(GetLoadAddress());205if (!m_type.IsValid()) {206// Don't know how to report new & old values, since we couldn't make a207// scalar type for this watchpoint. This works around an assert in208// ValueObjectMemory::Create.209// FIXME: This should not happen, but if it does in some case we care about,210// we can go grab the value raw and print it as unsigned.211return false;212}213m_new_value_sp = ValueObjectMemory::Create(214exe_ctx.GetBestExecutionContextScope(), g_watch_name.GetStringRef(),215watch_address, m_type);216m_new_value_sp = m_new_value_sp->CreateConstantValue(g_watch_name);217return (m_new_value_sp && m_new_value_sp->GetError().Success());218}219220bool Watchpoint::WatchedValueReportable(const ExecutionContext &exe_ctx) {221if (!m_watch_modify || m_watch_read)222return true;223if (!m_type.IsValid())224return true;225226ConstString g_watch_name("$__lldb__watch_value");227Address watch_address(GetLoadAddress());228ValueObjectSP newest_valueobj_sp = ValueObjectMemory::Create(229exe_ctx.GetBestExecutionContextScope(), g_watch_name.GetStringRef(),230watch_address, m_type);231newest_valueobj_sp = newest_valueobj_sp->CreateConstantValue(g_watch_name);232Status error;233234DataExtractor new_data;235DataExtractor old_data;236237newest_valueobj_sp->GetData(new_data, error);238if (error.Fail())239return true;240m_new_value_sp->GetData(old_data, error);241if (error.Fail())242return true;243244if (new_data.GetByteSize() != old_data.GetByteSize() ||245new_data.GetByteSize() == 0)246return true;247248if (memcmp(new_data.GetDataStart(), old_data.GetDataStart(),249old_data.GetByteSize()) == 0)250return false; // Value has not changed, user requested modify watchpoint251252return true;253}254255// RETURNS - true if we should stop at this breakpoint, false if we256// should continue.257258bool Watchpoint::ShouldStop(StoppointCallbackContext *context) {259m_hit_counter.Increment();260261return IsEnabled();262}263264void Watchpoint::GetDescription(Stream *s, lldb::DescriptionLevel level) {265DumpWithLevel(s, level);266}267268void Watchpoint::Dump(Stream *s) const {269DumpWithLevel(s, lldb::eDescriptionLevelBrief);270}271272// If prefix is nullptr, we display the watch id and ignore the prefix273// altogether.274bool Watchpoint::DumpSnapshots(Stream *s, const char *prefix) const {275bool printed_anything = false;276277// For read watchpoints, don't display any before/after value changes.278if (m_watch_read && !m_watch_modify && !m_watch_write)279return printed_anything;280281s->Printf("\n");282s->Printf("Watchpoint %u hit:\n", GetID());283284StreamString values_ss;285if (prefix)286values_ss.Indent(prefix);287288if (m_old_value_sp) {289if (auto *old_value_cstr = m_old_value_sp->GetValueAsCString()) {290values_ss.Printf("old value: %s", old_value_cstr);291} else {292if (auto *old_summary_cstr = m_old_value_sp->GetSummaryAsCString())293values_ss.Printf("old value: %s", old_summary_cstr);294else {295StreamString strm;296DumpValueObjectOptions options;297options.SetUseDynamicType(eNoDynamicValues)298.SetHideRootType(true)299.SetHideRootName(true)300.SetHideName(true);301if (llvm::Error error = m_old_value_sp->Dump(strm, options))302strm << "error: " << toString(std::move(error));303304if (strm.GetData())305values_ss.Printf("old value: %s", strm.GetData());306}307}308}309310if (m_new_value_sp) {311if (values_ss.GetSize())312values_ss.Printf("\n");313314if (auto *new_value_cstr = m_new_value_sp->GetValueAsCString())315values_ss.Printf("new value: %s", new_value_cstr);316else {317if (auto *new_summary_cstr = m_new_value_sp->GetSummaryAsCString())318values_ss.Printf("new value: %s", new_summary_cstr);319else {320StreamString strm;321DumpValueObjectOptions options;322options.SetUseDynamicType(eNoDynamicValues)323.SetHideRootType(true)324.SetHideRootName(true)325.SetHideName(true);326if (llvm::Error error = m_new_value_sp->Dump(strm, options))327strm << "error: " << toString(std::move(error));328329if (strm.GetData())330values_ss.Printf("new value: %s", strm.GetData());331}332}333}334335if (values_ss.GetSize()) {336s->Printf("%s", values_ss.GetData());337printed_anything = true;338}339340return printed_anything;341}342343void Watchpoint::DumpWithLevel(Stream *s,344lldb::DescriptionLevel description_level) const {345if (s == nullptr)346return;347348assert(description_level >= lldb::eDescriptionLevelBrief &&349description_level <= lldb::eDescriptionLevelVerbose);350351s->Printf("Watchpoint %u: addr = 0x%8.8" PRIx64352" size = %u state = %s type = %s%s%s",353GetID(), GetLoadAddress(), m_byte_size,354IsEnabled() ? "enabled" : "disabled", m_watch_read ? "r" : "",355m_watch_write ? "w" : "", m_watch_modify ? "m" : "");356357if (description_level >= lldb::eDescriptionLevelFull) {358if (!m_decl_str.empty())359s->Printf("\n declare @ '%s'", m_decl_str.c_str());360if (!m_watch_spec_str.empty())361s->Printf("\n watchpoint spec = '%s'", m_watch_spec_str.c_str());362if (IsEnabled()) {363if (ProcessSP process_sp = m_target.GetProcessSP()) {364auto &resourcelist = process_sp->GetWatchpointResourceList();365size_t idx = 0;366s->Printf("\n watchpoint resources:");367for (WatchpointResourceSP &wpres : resourcelist.Sites()) {368if (wpres->ConstituentsContains(this)) {369s->Printf("\n #%zu: ", idx);370wpres->Dump(s);371}372idx++;373}374}375}376377// Dump the snapshots we have taken.378DumpSnapshots(s, " ");379380if (GetConditionText())381s->Printf("\n condition = '%s'", GetConditionText());382m_options.GetCallbackDescription(s, description_level);383}384385if (description_level >= lldb::eDescriptionLevelVerbose) {386s->Printf("\n hit_count = %-4u ignore_count = %-4u", GetHitCount(),387GetIgnoreCount());388}389}390391bool Watchpoint::IsEnabled() const { return m_enabled; }392393// Within StopInfo.cpp, we purposely turn on the ephemeral mode right before394// temporarily disable the watchpoint in order to perform possible watchpoint395// actions without triggering further watchpoint events. After the temporary396// disabled watchpoint is enabled, we then turn off the ephemeral mode.397398void Watchpoint::TurnOnEphemeralMode() { m_is_ephemeral = true; }399400void Watchpoint::TurnOffEphemeralMode() {401m_is_ephemeral = false;402// Leaving ephemeral mode, reset the m_disabled_count!403m_disabled_count = 0;404}405406bool Watchpoint::IsDisabledDuringEphemeralMode() {407return m_disabled_count > 1 && m_is_ephemeral;408}409410void Watchpoint::SetEnabled(bool enabled, bool notify) {411if (!enabled) {412if (m_is_ephemeral)413++m_disabled_count;414415// Don't clear the snapshots for now.416// Within StopInfo.cpp, we purposely do disable/enable watchpoint while417// performing watchpoint actions.418}419bool changed = enabled != m_enabled;420m_enabled = enabled;421if (notify && !m_is_ephemeral && changed)422SendWatchpointChangedEvent(enabled ? eWatchpointEventTypeEnabled423: eWatchpointEventTypeDisabled);424}425426void Watchpoint::SetWatchpointType(uint32_t type, bool notify) {427int old_watch_read = m_watch_read;428int old_watch_write = m_watch_write;429int old_watch_modify = m_watch_modify;430m_watch_read = (type & LLDB_WATCH_TYPE_READ) != 0;431m_watch_write = (type & LLDB_WATCH_TYPE_WRITE) != 0;432m_watch_modify = (type & LLDB_WATCH_TYPE_MODIFY) != 0;433if (notify &&434(old_watch_read != m_watch_read || old_watch_write != m_watch_write ||435old_watch_modify != m_watch_modify))436SendWatchpointChangedEvent(eWatchpointEventTypeTypeChanged);437}438439bool Watchpoint::WatchpointRead() const { return m_watch_read != 0; }440441bool Watchpoint::WatchpointWrite() const { return m_watch_write != 0; }442443bool Watchpoint::WatchpointModify() const { return m_watch_modify != 0; }444445uint32_t Watchpoint::GetIgnoreCount() const { return m_ignore_count; }446447void Watchpoint::SetIgnoreCount(uint32_t n) {448bool changed = m_ignore_count != n;449m_ignore_count = n;450if (changed)451SendWatchpointChangedEvent(eWatchpointEventTypeIgnoreChanged);452}453454bool Watchpoint::InvokeCallback(StoppointCallbackContext *context) {455return m_options.InvokeCallback(context, GetID());456}457458void Watchpoint::SetCondition(const char *condition) {459if (condition == nullptr || condition[0] == '\0') {460if (m_condition_up)461m_condition_up.reset();462} else {463// Pass nullptr for expr_prefix (no translation-unit level definitions).464Status error;465m_condition_up.reset(m_target.GetUserExpressionForLanguage(466condition, {}, {}, UserExpression::eResultTypeAny,467EvaluateExpressionOptions(), nullptr, error));468if (error.Fail()) {469// FIXME: Log something...470m_condition_up.reset();471}472}473SendWatchpointChangedEvent(eWatchpointEventTypeConditionChanged);474}475476const char *Watchpoint::GetConditionText() const {477if (m_condition_up)478return m_condition_up->GetUserText();479else480return nullptr;481}482483void Watchpoint::SendWatchpointChangedEvent(484lldb::WatchpointEventType eventKind) {485if (GetTarget().EventTypeHasListeners(486Target::eBroadcastBitWatchpointChanged)) {487auto data_sp =488std::make_shared<WatchpointEventData>(eventKind, shared_from_this());489GetTarget().BroadcastEvent(Target::eBroadcastBitWatchpointChanged, data_sp);490}491}492493Watchpoint::WatchpointEventData::WatchpointEventData(494WatchpointEventType sub_type, const WatchpointSP &new_watchpoint_sp)495: m_watchpoint_event(sub_type), m_new_watchpoint_sp(new_watchpoint_sp) {}496497Watchpoint::WatchpointEventData::~WatchpointEventData() = default;498499llvm::StringRef Watchpoint::WatchpointEventData::GetFlavorString() {500return "Watchpoint::WatchpointEventData";501}502503llvm::StringRef Watchpoint::WatchpointEventData::GetFlavor() const {504return WatchpointEventData::GetFlavorString();505}506507WatchpointSP &Watchpoint::WatchpointEventData::GetWatchpoint() {508return m_new_watchpoint_sp;509}510511WatchpointEventType512Watchpoint::WatchpointEventData::GetWatchpointEventType() const {513return m_watchpoint_event;514}515516void Watchpoint::WatchpointEventData::Dump(Stream *s) const {}517518const Watchpoint::WatchpointEventData *519Watchpoint::WatchpointEventData::GetEventDataFromEvent(const Event *event) {520if (event) {521const EventData *event_data = event->GetData();522if (event_data &&523event_data->GetFlavor() == WatchpointEventData::GetFlavorString())524return static_cast<const WatchpointEventData *>(event->GetData());525}526return nullptr;527}528529WatchpointEventType530Watchpoint::WatchpointEventData::GetWatchpointEventTypeFromEvent(531const EventSP &event_sp) {532const WatchpointEventData *data = GetEventDataFromEvent(event_sp.get());533534if (data == nullptr)535return eWatchpointEventTypeInvalidType;536else537return data->GetWatchpointEventType();538}539540WatchpointSP Watchpoint::WatchpointEventData::GetWatchpointFromEvent(541const EventSP &event_sp) {542WatchpointSP wp_sp;543544const WatchpointEventData *data = GetEventDataFromEvent(event_sp.get());545if (data)546wp_sp = data->m_new_watchpoint_sp;547548return wp_sp;549}550551552