| 1 | //===-- Watchpoint.cpp ----------------------------------------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #include "lldb/Breakpoint/Watchpoint.h" |
| 10 | |
| 11 | #include "lldb/Breakpoint/StoppointCallbackContext.h" |
| 12 | #include "lldb/Breakpoint/WatchpointResource.h" |
| 13 | #include "lldb/Core/Value.h" |
| 14 | #include "lldb/DataFormatters/DumpValueObjectOptions.h" |
| 15 | #include "lldb/Expression/UserExpression.h" |
| 16 | #include "lldb/Symbol/TypeSystem.h" |
| 17 | #include "lldb/Target/Process.h" |
| 18 | #include "lldb/Target/Target.h" |
| 19 | #include "lldb/Target/ThreadSpec.h" |
| 20 | #include "lldb/Utility/LLDBLog.h" |
| 21 | #include "lldb/Utility/Log.h" |
| 22 | #include "lldb/Utility/Stream.h" |
| 23 | #include "lldb/ValueObject/ValueObject.h" |
| 24 | #include "lldb/ValueObject/ValueObjectMemory.h" |
| 25 | |
| 26 | using namespace lldb; |
| 27 | using namespace lldb_private; |
| 28 | |
| 29 | Watchpoint::Watchpoint(Target &target, lldb::addr_t addr, uint32_t size, |
| 30 | const CompilerType *type, bool hardware) |
| 31 | : StoppointSite(0, addr, size, hardware), m_target(target), |
| 32 | m_enabled(false), m_is_hardware(hardware), m_is_watch_variable(false), |
| 33 | m_is_ephemeral(false), m_disabled_count(0), m_watch_read(0), |
| 34 | m_watch_write(0), m_watch_modify(0), m_ignore_count(0) { |
| 35 | |
| 36 | if (type && type->IsValid()) |
| 37 | m_type = *type; |
| 38 | else { |
| 39 | // If we don't have a known type, then we force it to unsigned int of the |
| 40 | // right size. |
| 41 | auto type_system_or_err = |
| 42 | target.GetScratchTypeSystemForLanguage(language: eLanguageTypeC); |
| 43 | if (auto err = type_system_or_err.takeError()) { |
| 44 | LLDB_LOG_ERROR(GetLog(LLDBLog::Watchpoints), std::move(err), |
| 45 | "Failed to set type: {0}" ); |
| 46 | } else { |
| 47 | if (auto ts = *type_system_or_err) { |
| 48 | if (size <= target.GetArchitecture().GetAddressByteSize()) { |
| 49 | m_type = |
| 50 | ts->GetBuiltinTypeForEncodingAndBitSize(encoding: eEncodingUint, bit_size: 8 * size); |
| 51 | } else { |
| 52 | CompilerType clang_uint8_type = |
| 53 | ts->GetBuiltinTypeForEncodingAndBitSize(encoding: eEncodingUint, bit_size: 8); |
| 54 | m_type = clang_uint8_type.GetArrayType(size); |
| 55 | } |
| 56 | } else |
| 57 | LLDB_LOG_ERROR(GetLog(LLDBLog::Watchpoints), std::move(err), |
| 58 | "Failed to set type: Typesystem is no longer live: {0}" ); |
| 59 | } |
| 60 | } |
| 61 | |
| 62 | // Set the initial value of the watched variable: |
| 63 | if (m_target.GetProcessSP()) { |
| 64 | ExecutionContext exe_ctx; |
| 65 | m_target.GetProcessSP()->CalculateExecutionContext(exe_ctx); |
| 66 | CaptureWatchedValue(exe_ctx); |
| 67 | } |
| 68 | } |
| 69 | |
| 70 | Watchpoint::~Watchpoint() = default; |
| 71 | |
| 72 | // This function is used when "baton" doesn't need to be freed |
| 73 | void Watchpoint::SetCallback(WatchpointHitCallback callback, void *baton, |
| 74 | bool is_synchronous) { |
| 75 | // The default "Baton" class will keep a copy of "baton" and won't free or |
| 76 | // delete it when it goes out of scope. |
| 77 | m_options.SetCallback(callback, baton_sp: std::make_shared<UntypedBaton>(args&: baton), |
| 78 | synchronous: is_synchronous); |
| 79 | |
| 80 | SendWatchpointChangedEvent(eventKind: eWatchpointEventTypeCommandChanged); |
| 81 | } |
| 82 | |
| 83 | // This function is used when a baton needs to be freed and therefore is |
| 84 | // contained in a "Baton" subclass. |
| 85 | void Watchpoint::SetCallback(WatchpointHitCallback callback, |
| 86 | const BatonSP &callback_baton_sp, |
| 87 | bool is_synchronous) { |
| 88 | m_options.SetCallback(callback, baton_sp: callback_baton_sp, synchronous: is_synchronous); |
| 89 | SendWatchpointChangedEvent(eventKind: eWatchpointEventTypeCommandChanged); |
| 90 | } |
| 91 | |
| 92 | bool Watchpoint::SetupVariableWatchpointDisabler(StackFrameSP frame_sp) const { |
| 93 | if (!frame_sp) |
| 94 | return false; |
| 95 | |
| 96 | ThreadSP thread_sp = frame_sp->GetThread(); |
| 97 | if (!thread_sp) |
| 98 | return false; |
| 99 | |
| 100 | uint32_t return_frame_index = |
| 101 | thread_sp->GetSelectedFrameIndex(select_most_relevant: DoNoSelectMostRelevantFrame) + 1; |
| 102 | if (return_frame_index >= LLDB_INVALID_FRAME_ID) |
| 103 | return false; |
| 104 | |
| 105 | StackFrameSP return_frame_sp( |
| 106 | thread_sp->GetStackFrameAtIndex(idx: return_frame_index)); |
| 107 | if (!return_frame_sp) |
| 108 | return false; |
| 109 | |
| 110 | ExecutionContext exe_ctx(return_frame_sp); |
| 111 | TargetSP target_sp = exe_ctx.GetTargetSP(); |
| 112 | if (!target_sp) |
| 113 | return false; |
| 114 | |
| 115 | Address return_address(return_frame_sp->GetFrameCodeAddress()); |
| 116 | lldb::addr_t return_addr = return_address.GetLoadAddress(target: target_sp.get()); |
| 117 | if (return_addr == LLDB_INVALID_ADDRESS) |
| 118 | return false; |
| 119 | |
| 120 | BreakpointSP bp_sp = target_sp->CreateBreakpoint( |
| 121 | load_addr: return_addr, /*internal=*/true, /*request_hardware=*/false); |
| 122 | if (!bp_sp || !bp_sp->HasResolvedLocations()) |
| 123 | return false; |
| 124 | |
| 125 | auto wvc_up = std::make_unique<WatchpointVariableContext>(args: GetID(), args&: exe_ctx); |
| 126 | auto baton_sp = std::make_shared<WatchpointVariableBaton>(args: std::move(wvc_up)); |
| 127 | bp_sp->SetCallback(callback: VariableWatchpointDisabler, callback_baton_sp: baton_sp); |
| 128 | bp_sp->SetOneShot(true); |
| 129 | bp_sp->SetBreakpointKind("variable watchpoint disabler" ); |
| 130 | return true; |
| 131 | } |
| 132 | |
| 133 | bool Watchpoint::VariableWatchpointDisabler(void *baton, |
| 134 | StoppointCallbackContext *context, |
| 135 | user_id_t break_id, |
| 136 | user_id_t break_loc_id) { |
| 137 | assert(baton && "null baton" ); |
| 138 | if (!baton || !context) |
| 139 | return false; |
| 140 | |
| 141 | Log *log = GetLog(mask: LLDBLog::Watchpoints); |
| 142 | |
| 143 | WatchpointVariableContext *wvc = |
| 144 | static_cast<WatchpointVariableContext *>(baton); |
| 145 | |
| 146 | LLDB_LOGF(log, "called by breakpoint %" PRIu64 ".%" PRIu64, break_id, |
| 147 | break_loc_id); |
| 148 | |
| 149 | if (wvc->watch_id == LLDB_INVALID_WATCH_ID) |
| 150 | return false; |
| 151 | |
| 152 | TargetSP target_sp = context->exe_ctx_ref.GetTargetSP(); |
| 153 | if (!target_sp) |
| 154 | return false; |
| 155 | |
| 156 | ProcessSP process_sp = target_sp->GetProcessSP(); |
| 157 | if (!process_sp) |
| 158 | return false; |
| 159 | |
| 160 | WatchpointSP watch_sp = |
| 161 | target_sp->GetWatchpointList().FindByID(watchID: wvc->watch_id); |
| 162 | if (!watch_sp) |
| 163 | return false; |
| 164 | |
| 165 | if (wvc->exe_ctx == context->exe_ctx_ref) { |
| 166 | LLDB_LOGF(log, |
| 167 | "callback for watchpoint %" PRId32 |
| 168 | " matched internal breakpoint execution context" , |
| 169 | watch_sp->GetID()); |
| 170 | process_sp->DisableWatchpoint(wp_sp: watch_sp); |
| 171 | return false; |
| 172 | } |
| 173 | LLDB_LOGF(log, |
| 174 | "callback for watchpoint %" PRId32 |
| 175 | " didn't match internal breakpoint execution context" , |
| 176 | watch_sp->GetID()); |
| 177 | return false; |
| 178 | } |
| 179 | |
| 180 | void Watchpoint::ClearCallback() { |
| 181 | m_options.ClearCallback(); |
| 182 | SendWatchpointChangedEvent(eventKind: eWatchpointEventTypeCommandChanged); |
| 183 | } |
| 184 | |
| 185 | void Watchpoint::SetDeclInfo(const std::string &str) { m_decl_str = str; } |
| 186 | |
| 187 | std::string Watchpoint::GetWatchSpec() { return m_watch_spec_str; } |
| 188 | |
| 189 | void Watchpoint::SetWatchSpec(const std::string &str) { |
| 190 | m_watch_spec_str = str; |
| 191 | } |
| 192 | |
| 193 | bool Watchpoint::IsHardware() const { |
| 194 | lldbassert(m_is_hardware || !HardwareRequired()); |
| 195 | return m_is_hardware; |
| 196 | } |
| 197 | |
| 198 | bool Watchpoint::IsWatchVariable() const { return m_is_watch_variable; } |
| 199 | |
| 200 | void Watchpoint::SetWatchVariable(bool val) { m_is_watch_variable = val; } |
| 201 | |
| 202 | bool Watchpoint::CaptureWatchedValue(const ExecutionContext &exe_ctx) { |
| 203 | ConstString g_watch_name("$__lldb__watch_value" ); |
| 204 | m_old_value_sp = m_new_value_sp; |
| 205 | Address watch_address(GetLoadAddress()); |
| 206 | if (!m_type.IsValid()) { |
| 207 | // Don't know how to report new & old values, since we couldn't make a |
| 208 | // scalar type for this watchpoint. This works around an assert in |
| 209 | // ValueObjectMemory::Create. |
| 210 | // FIXME: This should not happen, but if it does in some case we care about, |
| 211 | // we can go grab the value raw and print it as unsigned. |
| 212 | return false; |
| 213 | } |
| 214 | m_new_value_sp = ValueObjectMemory::Create( |
| 215 | exe_scope: exe_ctx.GetBestExecutionContextScope(), name: g_watch_name.GetStringRef(), |
| 216 | address: watch_address, ast_type: m_type); |
| 217 | m_new_value_sp = m_new_value_sp->CreateConstantValue(name: g_watch_name); |
| 218 | return (m_new_value_sp && m_new_value_sp->GetError().Success()); |
| 219 | } |
| 220 | |
| 221 | bool Watchpoint::WatchedValueReportable(const ExecutionContext &exe_ctx) { |
| 222 | if (!m_watch_modify || m_watch_read) |
| 223 | return true; |
| 224 | if (!m_type.IsValid()) |
| 225 | return true; |
| 226 | |
| 227 | ConstString g_watch_name("$__lldb__watch_value" ); |
| 228 | Address watch_address(GetLoadAddress()); |
| 229 | ValueObjectSP newest_valueobj_sp = ValueObjectMemory::Create( |
| 230 | exe_scope: exe_ctx.GetBestExecutionContextScope(), name: g_watch_name.GetStringRef(), |
| 231 | address: watch_address, ast_type: m_type); |
| 232 | newest_valueobj_sp = newest_valueobj_sp->CreateConstantValue(name: g_watch_name); |
| 233 | Status error; |
| 234 | |
| 235 | DataExtractor new_data; |
| 236 | DataExtractor old_data; |
| 237 | |
| 238 | newest_valueobj_sp->GetData(data&: new_data, error); |
| 239 | if (error.Fail()) |
| 240 | return true; |
| 241 | m_new_value_sp->GetData(data&: old_data, error); |
| 242 | if (error.Fail()) |
| 243 | return true; |
| 244 | |
| 245 | if (new_data.GetByteSize() != old_data.GetByteSize() || |
| 246 | new_data.GetByteSize() == 0) |
| 247 | return true; |
| 248 | |
| 249 | if (memcmp(s1: new_data.GetDataStart(), s2: old_data.GetDataStart(), |
| 250 | n: old_data.GetByteSize()) == 0) |
| 251 | return false; // Value has not changed, user requested modify watchpoint |
| 252 | |
| 253 | return true; |
| 254 | } |
| 255 | |
| 256 | // RETURNS - true if we should stop at this breakpoint, false if we |
| 257 | // should continue. |
| 258 | |
| 259 | bool Watchpoint::ShouldStop(StoppointCallbackContext *context) { |
| 260 | m_hit_counter.Increment(); |
| 261 | |
| 262 | return IsEnabled(); |
| 263 | } |
| 264 | |
| 265 | void Watchpoint::GetDescription(Stream *s, lldb::DescriptionLevel level) { |
| 266 | DumpWithLevel(s, description_level: level); |
| 267 | } |
| 268 | |
| 269 | void Watchpoint::Dump(Stream *s) const { |
| 270 | DumpWithLevel(s, description_level: lldb::eDescriptionLevelBrief); |
| 271 | } |
| 272 | |
| 273 | // If prefix is nullptr, we display the watch id and ignore the prefix |
| 274 | // altogether. |
| 275 | bool Watchpoint::DumpSnapshots(Stream *s, const char *prefix) const { |
| 276 | bool printed_anything = false; |
| 277 | |
| 278 | // For read watchpoints, don't display any before/after value changes. |
| 279 | if (m_watch_read && !m_watch_modify && !m_watch_write) |
| 280 | return printed_anything; |
| 281 | |
| 282 | s->Printf(format: "\n" ); |
| 283 | s->Printf(format: "Watchpoint %u hit:\n" , GetID()); |
| 284 | |
| 285 | StreamString values_ss; |
| 286 | if (prefix) |
| 287 | values_ss.Indent(s: prefix); |
| 288 | |
| 289 | if (m_old_value_sp) { |
| 290 | if (auto *old_value_cstr = m_old_value_sp->GetValueAsCString()) { |
| 291 | values_ss.Printf(format: "old value: %s" , old_value_cstr); |
| 292 | } else { |
| 293 | if (auto *old_summary_cstr = m_old_value_sp->GetSummaryAsCString()) |
| 294 | values_ss.Printf(format: "old value: %s" , old_summary_cstr); |
| 295 | else { |
| 296 | StreamString strm; |
| 297 | DumpValueObjectOptions options; |
| 298 | options.SetUseDynamicType(eNoDynamicValues) |
| 299 | .SetHideRootType(true) |
| 300 | .SetHideRootName(true) |
| 301 | .SetHideName(true); |
| 302 | if (llvm::Error error = m_old_value_sp->Dump(s&: strm, options)) |
| 303 | strm << "error: " << toString(E: std::move(error)); |
| 304 | |
| 305 | if (strm.GetData()) |
| 306 | values_ss.Printf(format: "old value: %s" , strm.GetData()); |
| 307 | } |
| 308 | } |
| 309 | } |
| 310 | |
| 311 | if (m_new_value_sp) { |
| 312 | if (values_ss.GetSize()) |
| 313 | values_ss.Printf(format: "\n" ); |
| 314 | |
| 315 | if (auto *new_value_cstr = m_new_value_sp->GetValueAsCString()) |
| 316 | values_ss.Printf(format: "new value: %s" , new_value_cstr); |
| 317 | else { |
| 318 | if (auto *new_summary_cstr = m_new_value_sp->GetSummaryAsCString()) |
| 319 | values_ss.Printf(format: "new value: %s" , new_summary_cstr); |
| 320 | else { |
| 321 | StreamString strm; |
| 322 | DumpValueObjectOptions options; |
| 323 | options.SetUseDynamicType(eNoDynamicValues) |
| 324 | .SetHideRootType(true) |
| 325 | .SetHideRootName(true) |
| 326 | .SetHideName(true); |
| 327 | if (llvm::Error error = m_new_value_sp->Dump(s&: strm, options)) |
| 328 | strm << "error: " << toString(E: std::move(error)); |
| 329 | |
| 330 | if (strm.GetData()) |
| 331 | values_ss.Printf(format: "new value: %s" , strm.GetData()); |
| 332 | } |
| 333 | } |
| 334 | } |
| 335 | |
| 336 | if (values_ss.GetSize()) { |
| 337 | s->Printf(format: "%s" , values_ss.GetData()); |
| 338 | printed_anything = true; |
| 339 | } |
| 340 | |
| 341 | return printed_anything; |
| 342 | } |
| 343 | |
| 344 | void Watchpoint::DumpWithLevel(Stream *s, |
| 345 | lldb::DescriptionLevel description_level) const { |
| 346 | if (s == nullptr) |
| 347 | return; |
| 348 | |
| 349 | assert(description_level >= lldb::eDescriptionLevelBrief && |
| 350 | description_level <= lldb::eDescriptionLevelVerbose); |
| 351 | |
| 352 | s->Printf(format: "Watchpoint %u: addr = 0x%8.8" PRIx64 |
| 353 | " size = %u state = %s type = %s%s%s" , |
| 354 | GetID(), GetLoadAddress(), m_byte_size, |
| 355 | IsEnabled() ? "enabled" : "disabled" , m_watch_read ? "r" : "" , |
| 356 | m_watch_write ? "w" : "" , m_watch_modify ? "m" : "" ); |
| 357 | |
| 358 | if (description_level >= lldb::eDescriptionLevelFull) { |
| 359 | if (!m_decl_str.empty()) |
| 360 | s->Printf(format: "\n declare @ '%s'" , m_decl_str.c_str()); |
| 361 | if (!m_watch_spec_str.empty()) |
| 362 | s->Printf(format: "\n watchpoint spec = '%s'" , m_watch_spec_str.c_str()); |
| 363 | if (IsEnabled()) { |
| 364 | if (ProcessSP process_sp = m_target.GetProcessSP()) { |
| 365 | auto &resourcelist = process_sp->GetWatchpointResourceList(); |
| 366 | size_t idx = 0; |
| 367 | s->Printf(format: "\n watchpoint resources:" ); |
| 368 | for (WatchpointResourceSP &wpres : resourcelist.Sites()) { |
| 369 | if (wpres->ConstituentsContains(wp: this)) { |
| 370 | s->Printf(format: "\n #%zu: " , idx); |
| 371 | wpres->Dump(s); |
| 372 | } |
| 373 | idx++; |
| 374 | } |
| 375 | } |
| 376 | } |
| 377 | |
| 378 | // Dump the snapshots we have taken. |
| 379 | DumpSnapshots(s, prefix: " " ); |
| 380 | |
| 381 | if (GetConditionText()) |
| 382 | s->Printf(format: "\n condition = '%s'" , GetConditionText()); |
| 383 | m_options.GetCallbackDescription(s, level: description_level); |
| 384 | } |
| 385 | |
| 386 | if (description_level >= lldb::eDescriptionLevelVerbose) { |
| 387 | s->Printf(format: "\n hit_count = %-4u ignore_count = %-4u" , GetHitCount(), |
| 388 | GetIgnoreCount()); |
| 389 | } |
| 390 | } |
| 391 | |
| 392 | bool Watchpoint::IsEnabled() const { return m_enabled; } |
| 393 | |
| 394 | // Within StopInfo.cpp, we purposely turn on the ephemeral mode right before |
| 395 | // temporarily disable the watchpoint in order to perform possible watchpoint |
| 396 | // actions without triggering further watchpoint events. After the temporary |
| 397 | // disabled watchpoint is enabled, we then turn off the ephemeral mode. |
| 398 | |
| 399 | void Watchpoint::TurnOnEphemeralMode() { m_is_ephemeral = true; } |
| 400 | |
| 401 | void Watchpoint::TurnOffEphemeralMode() { |
| 402 | m_is_ephemeral = false; |
| 403 | // Leaving ephemeral mode, reset the m_disabled_count! |
| 404 | m_disabled_count = 0; |
| 405 | } |
| 406 | |
| 407 | bool Watchpoint::IsDisabledDuringEphemeralMode() { |
| 408 | return m_disabled_count > 1 && m_is_ephemeral; |
| 409 | } |
| 410 | |
| 411 | void Watchpoint::SetEnabled(bool enabled, bool notify) { |
| 412 | if (!enabled) { |
| 413 | if (m_is_ephemeral) |
| 414 | ++m_disabled_count; |
| 415 | |
| 416 | // Don't clear the snapshots for now. |
| 417 | // Within StopInfo.cpp, we purposely do disable/enable watchpoint while |
| 418 | // performing watchpoint actions. |
| 419 | } |
| 420 | bool changed = enabled != m_enabled; |
| 421 | m_enabled = enabled; |
| 422 | if (notify && !m_is_ephemeral && changed) |
| 423 | SendWatchpointChangedEvent(eventKind: enabled ? eWatchpointEventTypeEnabled |
| 424 | : eWatchpointEventTypeDisabled); |
| 425 | } |
| 426 | |
| 427 | void Watchpoint::SetWatchpointType(uint32_t type, bool notify) { |
| 428 | int old_watch_read = m_watch_read; |
| 429 | int old_watch_write = m_watch_write; |
| 430 | int old_watch_modify = m_watch_modify; |
| 431 | m_watch_read = (type & LLDB_WATCH_TYPE_READ) != 0; |
| 432 | m_watch_write = (type & LLDB_WATCH_TYPE_WRITE) != 0; |
| 433 | m_watch_modify = (type & LLDB_WATCH_TYPE_MODIFY) != 0; |
| 434 | if (notify && |
| 435 | (old_watch_read != m_watch_read || old_watch_write != m_watch_write || |
| 436 | old_watch_modify != m_watch_modify)) |
| 437 | SendWatchpointChangedEvent(eventKind: eWatchpointEventTypeTypeChanged); |
| 438 | } |
| 439 | |
| 440 | bool Watchpoint::WatchpointRead() const { return m_watch_read != 0; } |
| 441 | |
| 442 | bool Watchpoint::WatchpointWrite() const { return m_watch_write != 0; } |
| 443 | |
| 444 | bool Watchpoint::WatchpointModify() const { return m_watch_modify != 0; } |
| 445 | |
| 446 | uint32_t Watchpoint::GetIgnoreCount() const { return m_ignore_count; } |
| 447 | |
| 448 | void Watchpoint::SetIgnoreCount(uint32_t n) { |
| 449 | bool changed = m_ignore_count != n; |
| 450 | m_ignore_count = n; |
| 451 | if (changed) |
| 452 | SendWatchpointChangedEvent(eventKind: eWatchpointEventTypeIgnoreChanged); |
| 453 | } |
| 454 | |
| 455 | bool Watchpoint::InvokeCallback(StoppointCallbackContext *context) { |
| 456 | return m_options.InvokeCallback(context, watch_id: GetID()); |
| 457 | } |
| 458 | |
| 459 | void Watchpoint::SetCondition(const char *condition) { |
| 460 | if (condition == nullptr || condition[0] == '\0') { |
| 461 | if (m_condition_up) |
| 462 | m_condition_up.reset(); |
| 463 | } else { |
| 464 | // Pass nullptr for expr_prefix (no translation-unit level definitions). |
| 465 | Status error; |
| 466 | m_condition_up.reset(p: m_target.GetUserExpressionForLanguage( |
| 467 | expr: condition, prefix: {}, language: {}, desired_type: UserExpression::eResultTypeAny, |
| 468 | options: EvaluateExpressionOptions(), ctx_obj: nullptr, error)); |
| 469 | if (error.Fail()) { |
| 470 | // FIXME: Log something... |
| 471 | m_condition_up.reset(); |
| 472 | } |
| 473 | } |
| 474 | SendWatchpointChangedEvent(eventKind: eWatchpointEventTypeConditionChanged); |
| 475 | } |
| 476 | |
| 477 | const char *Watchpoint::GetConditionText() const { |
| 478 | if (m_condition_up) |
| 479 | return m_condition_up->GetUserText(); |
| 480 | else |
| 481 | return nullptr; |
| 482 | } |
| 483 | |
| 484 | void Watchpoint::SendWatchpointChangedEvent( |
| 485 | lldb::WatchpointEventType eventKind) { |
| 486 | if (GetTarget().EventTypeHasListeners( |
| 487 | event_type: Target::eBroadcastBitWatchpointChanged)) { |
| 488 | auto data_sp = |
| 489 | std::make_shared<WatchpointEventData>(args&: eventKind, args: shared_from_this()); |
| 490 | GetTarget().BroadcastEvent(event_type: Target::eBroadcastBitWatchpointChanged, event_data_sp: data_sp); |
| 491 | } |
| 492 | } |
| 493 | |
| 494 | Watchpoint::WatchpointEventData::WatchpointEventData( |
| 495 | WatchpointEventType sub_type, const WatchpointSP &new_watchpoint_sp) |
| 496 | : m_watchpoint_event(sub_type), m_new_watchpoint_sp(new_watchpoint_sp) {} |
| 497 | |
| 498 | Watchpoint::WatchpointEventData::~WatchpointEventData() = default; |
| 499 | |
| 500 | llvm::StringRef Watchpoint::WatchpointEventData::GetFlavorString() { |
| 501 | return "Watchpoint::WatchpointEventData" ; |
| 502 | } |
| 503 | |
| 504 | llvm::StringRef Watchpoint::WatchpointEventData::GetFlavor() const { |
| 505 | return WatchpointEventData::GetFlavorString(); |
| 506 | } |
| 507 | |
| 508 | WatchpointSP &Watchpoint::WatchpointEventData::GetWatchpoint() { |
| 509 | return m_new_watchpoint_sp; |
| 510 | } |
| 511 | |
| 512 | WatchpointEventType |
| 513 | Watchpoint::WatchpointEventData::GetWatchpointEventType() const { |
| 514 | return m_watchpoint_event; |
| 515 | } |
| 516 | |
| 517 | void Watchpoint::WatchpointEventData::Dump(Stream *s) const {} |
| 518 | |
| 519 | const Watchpoint::WatchpointEventData * |
| 520 | Watchpoint::WatchpointEventData::GetEventDataFromEvent(const Event *event) { |
| 521 | if (event) { |
| 522 | const EventData *event_data = event->GetData(); |
| 523 | if (event_data && |
| 524 | event_data->GetFlavor() == WatchpointEventData::GetFlavorString()) |
| 525 | return static_cast<const WatchpointEventData *>(event->GetData()); |
| 526 | } |
| 527 | return nullptr; |
| 528 | } |
| 529 | |
| 530 | WatchpointEventType |
| 531 | Watchpoint::WatchpointEventData::GetWatchpointEventTypeFromEvent( |
| 532 | const EventSP &event_sp) { |
| 533 | const WatchpointEventData *data = GetEventDataFromEvent(event: event_sp.get()); |
| 534 | |
| 535 | if (data == nullptr) |
| 536 | return eWatchpointEventTypeInvalidType; |
| 537 | else |
| 538 | return data->GetWatchpointEventType(); |
| 539 | } |
| 540 | |
| 541 | WatchpointSP Watchpoint::WatchpointEventData::GetWatchpointFromEvent( |
| 542 | const EventSP &event_sp) { |
| 543 | WatchpointSP wp_sp; |
| 544 | |
| 545 | const WatchpointEventData *data = GetEventDataFromEvent(event: event_sp.get()); |
| 546 | if (data) |
| 547 | wp_sp = data->m_new_watchpoint_sp; |
| 548 | |
| 549 | return wp_sp; |
| 550 | } |
| 551 | |