1185 lines · cpp
1//===-- CommandObjectWatchpoint.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-exception6//7//===----------------------------------------------------------------------===//8 9#include "CommandObjectWatchpoint.h"10#include "CommandObjectWatchpointCommand.h"11 12#include <memory>13#include <vector>14 15#include "llvm/ADT/StringRef.h"16 17#include "lldb/Breakpoint/Watchpoint.h"18#include "lldb/Breakpoint/WatchpointList.h"19#include "lldb/Host/OptionParser.h"20#include "lldb/Interpreter/CommandInterpreter.h"21#include "lldb/Interpreter/CommandOptionArgumentTable.h"22#include "lldb/Interpreter/CommandReturnObject.h"23#include "lldb/Symbol/Function.h"24#include "lldb/Symbol/Variable.h"25#include "lldb/Symbol/VariableList.h"26#include "lldb/Target/StackFrame.h"27#include "lldb/Target/Target.h"28#include "lldb/Utility/StreamString.h"29#include "lldb/ValueObject/ValueObject.h"30 31using namespace lldb;32using namespace lldb_private;33 34static void AddWatchpointDescription(Stream &s, Watchpoint &wp,35 lldb::DescriptionLevel level) {36 s.IndentMore();37 wp.GetDescription(&s, level);38 s.IndentLess();39 s.EOL();40}41 42static bool CheckTargetForWatchpointOperations(Target &target,43 CommandReturnObject &result) {44 bool process_is_valid =45 target.GetProcessSP() && target.GetProcessSP()->IsAlive();46 if (!process_is_valid) {47 result.AppendError("there's no process or it is not alive");48 return false;49 }50 // Target passes our checks, return true.51 return true;52}53 54// Equivalent class: {"-", "to", "To", "TO"} of range specifier array.55static const char *RSA[4] = {"-", "to", "To", "TO"};56 57// Return the index to RSA if found; otherwise -1 is returned.58static int32_t WithRSAIndex(llvm::StringRef Arg) {59 60 uint32_t i;61 for (i = 0; i < 4; ++i)62 if (Arg.contains(RSA[i]))63 return i;64 return -1;65}66 67// Return true if wp_ids is successfully populated with the watch ids. False68// otherwise.69bool CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(70 Target &target, Args &args, std::vector<uint32_t> &wp_ids) {71 // Pre-condition: args.GetArgumentCount() > 0.72 if (args.GetArgumentCount() == 0) {73 WatchpointSP watch_sp = target.GetLastCreatedWatchpoint();74 if (watch_sp) {75 wp_ids.push_back(watch_sp->GetID());76 return true;77 } else78 return false;79 }80 81 llvm::StringRef Minus("-");82 std::vector<llvm::StringRef> StrRefArgs;83 llvm::StringRef first;84 llvm::StringRef second;85 size_t i;86 int32_t idx;87 // Go through the arguments and make a canonical form of arg list containing88 // only numbers with possible "-" in between.89 for (auto &entry : args.entries()) {90 if ((idx = WithRSAIndex(entry.ref())) == -1) {91 StrRefArgs.push_back(entry.ref());92 continue;93 }94 // The Arg contains the range specifier, split it, then.95 std::tie(first, second) = entry.ref().split(RSA[idx]);96 if (!first.empty())97 StrRefArgs.push_back(first);98 StrRefArgs.push_back(Minus);99 if (!second.empty())100 StrRefArgs.push_back(second);101 }102 // Now process the canonical list and fill in the vector of uint32_t's. If103 // there is any error, return false and the client should ignore wp_ids.104 uint32_t beg, end, id;105 size_t size = StrRefArgs.size();106 bool in_range = false;107 for (i = 0; i < size; ++i) {108 llvm::StringRef Arg = StrRefArgs[i];109 if (in_range) {110 // Look for the 'end' of the range. Note StringRef::getAsInteger()111 // returns true to signify error while parsing.112 if (Arg.getAsInteger(0, end))113 return false;114 // Found a range! Now append the elements.115 for (id = beg; id <= end; ++id)116 wp_ids.push_back(id);117 in_range = false;118 continue;119 }120 if (i < (size - 1) && StrRefArgs[i + 1] == Minus) {121 if (Arg.getAsInteger(0, beg))122 return false;123 // Turn on the in_range flag, we are looking for end of range next.124 ++i;125 in_range = true;126 continue;127 }128 // Otherwise, we have a simple ID. Just append it.129 if (Arg.getAsInteger(0, beg))130 return false;131 wp_ids.push_back(beg);132 }133 134 // It is an error if after the loop, we're still in_range.135 return !in_range;136}137 138// CommandObjectWatchpointList139 140// CommandObjectWatchpointList::Options141#pragma mark List::CommandOptions142#define LLDB_OPTIONS_watchpoint_list143#include "CommandOptions.inc"144 145#pragma mark List146 147class CommandObjectWatchpointList : public CommandObjectParsed {148public:149 CommandObjectWatchpointList(CommandInterpreter &interpreter)150 : CommandObjectParsed(151 interpreter, "watchpoint list",152 "List all watchpoints at configurable levels of detail.", nullptr,153 eCommandRequiresTarget) {154 CommandObject::AddIDsArgumentData(eWatchpointArgs);155 }156 157 ~CommandObjectWatchpointList() override = default;158 159 Options *GetOptions() override { return &m_options; }160 161 class CommandOptions : public Options {162 public:163 CommandOptions() = default;164 165 ~CommandOptions() override = default;166 167 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,168 ExecutionContext *execution_context) override {169 Status error;170 const int short_option = m_getopt_table[option_idx].val;171 172 switch (short_option) {173 case 'b':174 m_level = lldb::eDescriptionLevelBrief;175 break;176 case 'f':177 m_level = lldb::eDescriptionLevelFull;178 break;179 case 'v':180 m_level = lldb::eDescriptionLevelVerbose;181 break;182 default:183 llvm_unreachable("Unimplemented option");184 }185 186 return error;187 }188 189 void OptionParsingStarting(ExecutionContext *execution_context) override {190 m_level = lldb::eDescriptionLevelFull;191 }192 193 llvm::ArrayRef<OptionDefinition> GetDefinitions() override {194 return llvm::ArrayRef(g_watchpoint_list_options);195 }196 197 // Instance variables to hold the values for command options.198 199 lldb::DescriptionLevel m_level = lldb::eDescriptionLevelBrief;200 };201 202protected:203 void DoExecute(Args &command, CommandReturnObject &result) override {204 Target &target = GetTarget();205 206 if (ProcessSP process_sp = target.GetProcessSP()) {207 if (process_sp->IsAlive()) {208 std::optional<uint32_t> num_supported_hardware_watchpoints =209 process_sp->GetWatchpointSlotCount();210 211 if (num_supported_hardware_watchpoints)212 result.AppendMessageWithFormat(213 "Number of supported hardware watchpoints: %u\n",214 *num_supported_hardware_watchpoints);215 }216 }217 218 const WatchpointList &watchpoints = target.GetWatchpointList();219 220 std::unique_lock<std::recursive_mutex> lock;221 target.GetWatchpointList().GetListMutex(lock);222 223 size_t num_watchpoints = watchpoints.GetSize();224 225 if (num_watchpoints == 0) {226 result.AppendMessage("No watchpoints currently set.");227 result.SetStatus(eReturnStatusSuccessFinishNoResult);228 return;229 }230 231 Stream &output_stream = result.GetOutputStream();232 233 if (command.GetArgumentCount() == 0) {234 // No watchpoint selected; show info about all currently set watchpoints.235 result.AppendMessage("Current watchpoints:");236 for (size_t i = 0; i < num_watchpoints; ++i) {237 WatchpointSP watch_sp = watchpoints.GetByIndex(i);238 AddWatchpointDescription(output_stream, *watch_sp, m_options.m_level);239 }240 result.SetStatus(eReturnStatusSuccessFinishNoResult);241 } else {242 // Particular watchpoints selected; enable them.243 std::vector<uint32_t> wp_ids;244 if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(245 target, command, wp_ids)) {246 result.AppendError("invalid watchpoints specification");247 return;248 }249 250 const size_t size = wp_ids.size();251 for (size_t i = 0; i < size; ++i) {252 WatchpointSP watch_sp = watchpoints.FindByID(wp_ids[i]);253 if (watch_sp)254 AddWatchpointDescription(output_stream, *watch_sp, m_options.m_level);255 result.SetStatus(eReturnStatusSuccessFinishNoResult);256 }257 }258 }259 260private:261 CommandOptions m_options;262};263 264// CommandObjectWatchpointEnable265#pragma mark Enable266 267class CommandObjectWatchpointEnable : public CommandObjectParsed {268public:269 CommandObjectWatchpointEnable(CommandInterpreter &interpreter)270 : CommandObjectParsed(interpreter, "enable",271 "Enable the specified disabled watchpoint(s). If "272 "no watchpoints are specified, enable all of them.",273 nullptr, eCommandRequiresTarget) {274 CommandObject::AddIDsArgumentData(eWatchpointArgs);275 }276 277 ~CommandObjectWatchpointEnable() override = default;278 279 void280 HandleArgumentCompletion(CompletionRequest &request,281 OptionElementVector &opt_element_vector) override {282 lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(283 GetCommandInterpreter(), lldb::eWatchpointIDCompletion, request,284 nullptr);285 }286 287protected:288 void DoExecute(Args &command, CommandReturnObject &result) override {289 Target &target = GetTarget();290 if (!CheckTargetForWatchpointOperations(target, result))291 return;292 293 std::unique_lock<std::recursive_mutex> lock;294 target.GetWatchpointList().GetListMutex(lock);295 296 const WatchpointList &watchpoints = target.GetWatchpointList();297 298 size_t num_watchpoints = watchpoints.GetSize();299 300 if (num_watchpoints == 0) {301 result.AppendError("no watchpoints exist to be enabled");302 return;303 }304 305 if (command.GetArgumentCount() == 0) {306 // No watchpoint selected; enable all currently set watchpoints.307 target.EnableAllWatchpoints();308 result.AppendMessageWithFormat("All watchpoints enabled. (%" PRIu64309 " watchpoints)\n",310 (uint64_t)num_watchpoints);311 result.SetStatus(eReturnStatusSuccessFinishNoResult);312 } else {313 // Particular watchpoints selected; enable them.314 std::vector<uint32_t> wp_ids;315 if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(316 target, command, wp_ids)) {317 result.AppendError("invalid watchpoints specification");318 return;319 }320 321 int count = 0;322 const size_t size = wp_ids.size();323 for (size_t i = 0; i < size; ++i)324 if (target.EnableWatchpointByID(wp_ids[i]))325 ++count;326 result.AppendMessageWithFormat("%d watchpoints enabled.\n", count);327 result.SetStatus(eReturnStatusSuccessFinishNoResult);328 }329 }330};331 332// CommandObjectWatchpointDisable333#pragma mark Disable334 335class CommandObjectWatchpointDisable : public CommandObjectParsed {336public:337 CommandObjectWatchpointDisable(CommandInterpreter &interpreter)338 : CommandObjectParsed(interpreter, "watchpoint disable",339 "Disable the specified watchpoint(s) without "340 "removing it/them. If no watchpoints are "341 "specified, disable them all.",342 nullptr, eCommandRequiresTarget) {343 CommandObject::AddIDsArgumentData(eWatchpointArgs);344 }345 346 ~CommandObjectWatchpointDisable() override = default;347 348 void349 HandleArgumentCompletion(CompletionRequest &request,350 OptionElementVector &opt_element_vector) override {351 lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(352 GetCommandInterpreter(), lldb::eWatchpointIDCompletion, request,353 nullptr);354 }355 356protected:357 void DoExecute(Args &command, CommandReturnObject &result) override {358 Target &target = GetTarget();359 if (!CheckTargetForWatchpointOperations(target, result))360 return;361 362 std::unique_lock<std::recursive_mutex> lock;363 target.GetWatchpointList().GetListMutex(lock);364 365 const WatchpointList &watchpoints = target.GetWatchpointList();366 size_t num_watchpoints = watchpoints.GetSize();367 368 if (num_watchpoints == 0) {369 result.AppendError("no watchpoints exist to be disabled");370 return;371 }372 373 if (command.GetArgumentCount() == 0) {374 // No watchpoint selected; disable all currently set watchpoints.375 if (target.DisableAllWatchpoints()) {376 result.AppendMessageWithFormat("All watchpoints disabled. (%" PRIu64377 " watchpoints)\n",378 (uint64_t)num_watchpoints);379 result.SetStatus(eReturnStatusSuccessFinishNoResult);380 } else {381 result.AppendError("Disable all watchpoints failed\n");382 }383 } else {384 // Particular watchpoints selected; disable them.385 std::vector<uint32_t> wp_ids;386 if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(387 target, command, wp_ids)) {388 result.AppendError("invalid watchpoints specification");389 return;390 }391 392 int count = 0;393 const size_t size = wp_ids.size();394 for (size_t i = 0; i < size; ++i)395 if (target.DisableWatchpointByID(wp_ids[i]))396 ++count;397 result.AppendMessageWithFormat("%d watchpoints disabled.\n", count);398 result.SetStatus(eReturnStatusSuccessFinishNoResult);399 }400 }401};402 403// CommandObjectWatchpointDelete404#define LLDB_OPTIONS_watchpoint_delete405#include "CommandOptions.inc"406 407// CommandObjectWatchpointDelete408#pragma mark Delete409 410class CommandObjectWatchpointDelete : public CommandObjectParsed {411public:412 CommandObjectWatchpointDelete(CommandInterpreter &interpreter)413 : CommandObjectParsed(interpreter, "watchpoint delete",414 "Delete the specified watchpoint(s). If no "415 "watchpoints are specified, delete them all.",416 nullptr, eCommandRequiresTarget) {417 CommandObject::AddIDsArgumentData(eWatchpointArgs);418 }419 420 ~CommandObjectWatchpointDelete() override = default;421 422 void423 HandleArgumentCompletion(CompletionRequest &request,424 OptionElementVector &opt_element_vector) override {425 lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(426 GetCommandInterpreter(), lldb::eWatchpointIDCompletion, request,427 nullptr);428 }429 430 Options *GetOptions() override { return &m_options; }431 432 class CommandOptions : public Options {433 public:434 CommandOptions() = default;435 436 ~CommandOptions() override = default;437 438 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,439 ExecutionContext *execution_context) override {440 const int short_option = m_getopt_table[option_idx].val;441 442 switch (short_option) {443 case 'f':444 m_force = true;445 break;446 default:447 llvm_unreachable("Unimplemented option");448 }449 450 return {};451 }452 453 void OptionParsingStarting(ExecutionContext *execution_context) override {454 m_force = false;455 }456 457 llvm::ArrayRef<OptionDefinition> GetDefinitions() override {458 return llvm::ArrayRef(g_watchpoint_delete_options);459 }460 461 // Instance variables to hold the values for command options.462 bool m_force = false;463 };464 465protected:466 void DoExecute(Args &command, CommandReturnObject &result) override {467 Target &target = GetTarget();468 if (!CheckTargetForWatchpointOperations(target, result))469 return;470 471 std::unique_lock<std::recursive_mutex> lock;472 target.GetWatchpointList().GetListMutex(lock);473 474 const WatchpointList &watchpoints = target.GetWatchpointList();475 476 size_t num_watchpoints = watchpoints.GetSize();477 478 if (num_watchpoints == 0) {479 result.AppendError("no watchpoints exist to be deleted");480 return;481 }482 483 if (command.empty()) {484 if (!m_options.m_force &&485 !m_interpreter.Confirm(486 "About to delete all watchpoints, do you want to do that?",487 true)) {488 result.AppendMessage("Operation cancelled...");489 } else {490 target.RemoveAllWatchpoints();491 result.AppendMessageWithFormat("All watchpoints removed. (%" PRIu64492 " watchpoints)\n",493 (uint64_t)num_watchpoints);494 }495 result.SetStatus(eReturnStatusSuccessFinishNoResult);496 return;497 }498 499 // Particular watchpoints selected; delete them.500 std::vector<uint32_t> wp_ids;501 if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, command,502 wp_ids)) {503 result.AppendError("invalid watchpoints specification");504 return;505 }506 507 int count = 0;508 const size_t size = wp_ids.size();509 for (size_t i = 0; i < size; ++i)510 if (target.RemoveWatchpointByID(wp_ids[i]))511 ++count;512 result.AppendMessageWithFormat("%d watchpoints deleted.\n", count);513 result.SetStatus(eReturnStatusSuccessFinishNoResult);514 }515 516private:517 CommandOptions m_options;518};519 520// CommandObjectWatchpointIgnore521 522#pragma mark Ignore::CommandOptions523#define LLDB_OPTIONS_watchpoint_ignore524#include "CommandOptions.inc"525 526class CommandObjectWatchpointIgnore : public CommandObjectParsed {527public:528 CommandObjectWatchpointIgnore(CommandInterpreter &interpreter)529 : CommandObjectParsed(interpreter, "watchpoint ignore",530 "Set ignore count on the specified watchpoint(s). "531 "If no watchpoints are specified, set them all.",532 nullptr, eCommandRequiresTarget) {533 CommandObject::AddIDsArgumentData(eWatchpointArgs);534 }535 536 ~CommandObjectWatchpointIgnore() override = default;537 538 void539 HandleArgumentCompletion(CompletionRequest &request,540 OptionElementVector &opt_element_vector) override {541 lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(542 GetCommandInterpreter(), lldb::eWatchpointIDCompletion, request,543 nullptr);544 }545 546 Options *GetOptions() override { return &m_options; }547 548 class CommandOptions : public Options {549 public:550 CommandOptions() = default;551 552 ~CommandOptions() override = default;553 554 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,555 ExecutionContext *execution_context) override {556 Status error;557 const int short_option = m_getopt_table[option_idx].val;558 559 switch (short_option) {560 case 'i':561 if (option_arg.getAsInteger(0, m_ignore_count))562 error = Status::FromErrorStringWithFormat("invalid ignore count '%s'",563 option_arg.str().c_str());564 break;565 default:566 llvm_unreachable("Unimplemented option");567 }568 569 return error;570 }571 572 void OptionParsingStarting(ExecutionContext *execution_context) override {573 m_ignore_count = 0;574 }575 576 llvm::ArrayRef<OptionDefinition> GetDefinitions() override {577 return llvm::ArrayRef(g_watchpoint_ignore_options);578 }579 580 // Instance variables to hold the values for command options.581 582 uint32_t m_ignore_count = 0;583 };584 585protected:586 void DoExecute(Args &command, CommandReturnObject &result) override {587 Target &target = GetTarget();588 if (!CheckTargetForWatchpointOperations(target, result))589 return;590 591 std::unique_lock<std::recursive_mutex> lock;592 target.GetWatchpointList().GetListMutex(lock);593 594 const WatchpointList &watchpoints = target.GetWatchpointList();595 596 size_t num_watchpoints = watchpoints.GetSize();597 598 if (num_watchpoints == 0) {599 result.AppendError("no watchpoints exist to be ignored");600 return;601 }602 603 if (command.GetArgumentCount() == 0) {604 target.IgnoreAllWatchpoints(m_options.m_ignore_count);605 result.AppendMessageWithFormat("All watchpoints ignored. (%" PRIu64606 " watchpoints)\n",607 (uint64_t)num_watchpoints);608 result.SetStatus(eReturnStatusSuccessFinishNoResult);609 } else {610 // Particular watchpoints selected; ignore them.611 std::vector<uint32_t> wp_ids;612 if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(613 target, command, wp_ids)) {614 result.AppendError("invalid watchpoints specification");615 return;616 }617 618 int count = 0;619 const size_t size = wp_ids.size();620 for (size_t i = 0; i < size; ++i)621 if (target.IgnoreWatchpointByID(wp_ids[i], m_options.m_ignore_count))622 ++count;623 result.AppendMessageWithFormat("%d watchpoints ignored.\n", count);624 result.SetStatus(eReturnStatusSuccessFinishNoResult);625 }626 }627 628private:629 CommandOptions m_options;630};631 632// CommandObjectWatchpointModify633 634#pragma mark Modify::CommandOptions635#define LLDB_OPTIONS_watchpoint_modify636#include "CommandOptions.inc"637 638#pragma mark Modify639 640class CommandObjectWatchpointModify : public CommandObjectParsed {641public:642 CommandObjectWatchpointModify(CommandInterpreter &interpreter)643 : CommandObjectParsed(644 interpreter, "watchpoint modify",645 "Modify the options on a watchpoint or set of watchpoints in the "646 "executable. "647 "If no watchpoint is specified, act on the last created "648 "watchpoint. "649 "Passing an empty argument clears the modification.",650 nullptr, eCommandRequiresTarget) {651 CommandObject::AddIDsArgumentData(eWatchpointArgs);652 }653 654 ~CommandObjectWatchpointModify() override = default;655 656 void657 HandleArgumentCompletion(CompletionRequest &request,658 OptionElementVector &opt_element_vector) override {659 lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks(660 GetCommandInterpreter(), lldb::eWatchpointIDCompletion, request,661 nullptr);662 }663 664 Options *GetOptions() override { return &m_options; }665 666 class CommandOptions : public Options {667 public:668 CommandOptions() = default;669 670 ~CommandOptions() override = default;671 672 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,673 ExecutionContext *execution_context) override {674 Status error;675 const int short_option = m_getopt_table[option_idx].val;676 677 switch (short_option) {678 case 'c':679 m_condition = std::string(option_arg);680 m_condition_passed = true;681 break;682 default:683 llvm_unreachable("Unimplemented option");684 }685 686 return error;687 }688 689 void OptionParsingStarting(ExecutionContext *execution_context) override {690 m_condition.clear();691 m_condition_passed = false;692 }693 694 llvm::ArrayRef<OptionDefinition> GetDefinitions() override {695 return llvm::ArrayRef(g_watchpoint_modify_options);696 }697 698 // Instance variables to hold the values for command options.699 700 std::string m_condition;701 bool m_condition_passed = false;702 };703 704protected:705 void DoExecute(Args &command, CommandReturnObject &result) override {706 Target &target = GetTarget();707 if (!CheckTargetForWatchpointOperations(target, result))708 return;709 710 std::unique_lock<std::recursive_mutex> lock;711 target.GetWatchpointList().GetListMutex(lock);712 713 const WatchpointList &watchpoints = target.GetWatchpointList();714 715 size_t num_watchpoints = watchpoints.GetSize();716 717 if (num_watchpoints == 0) {718 result.AppendError("no watchpoints exist to be modified");719 return;720 }721 722 if (command.GetArgumentCount() == 0) {723 WatchpointSP watch_sp = target.GetLastCreatedWatchpoint();724 watch_sp->SetCondition(m_options.m_condition.c_str());725 result.SetStatus(eReturnStatusSuccessFinishNoResult);726 } else {727 // Particular watchpoints selected; set condition on them.728 std::vector<uint32_t> wp_ids;729 if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(730 target, command, wp_ids)) {731 result.AppendError("invalid watchpoints specification");732 return;733 }734 735 int count = 0;736 const size_t size = wp_ids.size();737 for (size_t i = 0; i < size; ++i) {738 WatchpointSP watch_sp = watchpoints.FindByID(wp_ids[i]);739 if (watch_sp) {740 watch_sp->SetCondition(m_options.m_condition.c_str());741 ++count;742 }743 }744 result.AppendMessageWithFormat("%d watchpoints modified.\n", count);745 result.SetStatus(eReturnStatusSuccessFinishNoResult);746 }747 }748 749private:750 CommandOptions m_options;751};752 753// CommandObjectWatchpointSetVariable754#pragma mark SetVariable755 756class CommandObjectWatchpointSetVariable : public CommandObjectParsed {757public:758 CommandObjectWatchpointSetVariable(CommandInterpreter &interpreter)759 : CommandObjectParsed(760 interpreter, "watchpoint set variable",761 "Set a watchpoint on a variable. "762 "Use the '-w' option to specify the type of watchpoint and "763 "the '-s' option to specify the byte size to watch for. "764 "If no '-w' option is specified, it defaults to modify. "765 "If no '-s' option is specified, it defaults to the variable's "766 "byte size. "767 "Note that there are limited hardware resources for watchpoints. "768 "If watchpoint setting fails, consider disable/delete existing "769 "ones "770 "to free up resources.",771 nullptr,772 eCommandRequiresFrame | eCommandTryTargetAPILock |773 eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) {774 SetHelpLong(775 R"(776Examples:777 778(lldb) watchpoint set variable -w read_write my_global_var779 780)"781 " Watches my_global_var for read/write access, with the region to watch \782corresponding to the byte size of the data type.");783 784 AddSimpleArgumentList(eArgTypeVarName);785 786 // Absorb the '-w' and '-s' options into our option group.787 m_option_group.Append(&m_option_watchpoint, LLDB_OPT_SET_1, LLDB_OPT_SET_1);788 m_option_group.Finalize();789 }790 791 ~CommandObjectWatchpointSetVariable() override = default;792 793 Options *GetOptions() override { return &m_option_group; }794 795protected:796 static size_t GetVariableCallback(void *baton, const char *name,797 VariableList &variable_list) {798 size_t old_size = variable_list.GetSize();799 Target *target = static_cast<Target *>(baton);800 if (target)801 target->GetImages().FindGlobalVariables(ConstString(name), UINT32_MAX,802 variable_list);803 return variable_list.GetSize() - old_size;804 }805 806 void DoExecute(Args &command, CommandReturnObject &result) override {807 Target &target = GetTarget();808 StackFrame *frame = m_exe_ctx.GetFramePtr();809 810 // If no argument is present, issue an error message. There's no way to811 // set a watchpoint.812 if (command.GetArgumentCount() <= 0) {813 result.AppendError("required argument missing; "814 "specify your program variable to watch for");815 return;816 }817 818 // If no '-w' is specified, default to '-w modify'.819 if (!m_option_watchpoint.watch_type_specified) {820 m_option_watchpoint.watch_type = OptionGroupWatchpoint::eWatchModify;821 }822 823 // We passed the sanity check for the command. Proceed to set the824 // watchpoint now.825 size_t size = 0;826 827 VariableSP var_sp;828 ValueObjectSP valobj_sp;829 Stream &output_stream = result.GetOutputStream();830 831 // A simple watch variable gesture allows only one argument.832 if (command.GetArgumentCount() != 1) {833 result.AppendError("specify exactly one variable to watch for");834 return;835 }836 837 // Things have checked out ok...838 Status error;839 uint32_t expr_path_options =840 StackFrame::eExpressionPathOptionCheckPtrVsMember |841 StackFrame::eExpressionPathOptionsAllowDirectIVarAccess;842 valobj_sp = frame->GetValueForVariableExpressionPath(843 command.GetArgumentAtIndex(0), eNoDynamicValues, expr_path_options,844 var_sp, error);845 846 if (!valobj_sp) {847 // Not in the frame; let's check the globals.848 849 VariableList variable_list;850 ValueObjectList valobj_list;851 852 Status error(Variable::GetValuesForVariableExpressionPath(853 command.GetArgumentAtIndex(0),854 m_exe_ctx.GetBestExecutionContextScope(), GetVariableCallback,855 &target, variable_list, valobj_list));856 857 if (valobj_list.GetSize())858 valobj_sp = valobj_list.GetValueObjectAtIndex(0);859 }860 861 CompilerType compiler_type;862 863 if (!valobj_sp) {864 const char *error_cstr = error.AsCString(nullptr);865 if (error_cstr)866 result.AppendError(error_cstr);867 else868 result.AppendErrorWithFormat("unable to find any variable "869 "expression path that matches '%s'",870 command.GetArgumentAtIndex(0));871 return;872 }873 auto [addr, addr_type] = valobj_sp->GetAddressOf(false);874 if (addr_type == eAddressTypeLoad) {875 // We're in business.876 // Find out the size of this variable.877 size =878 m_option_watchpoint.watch_size.GetCurrentValue() == 0879 ? llvm::expectedToOptional(valobj_sp->GetByteSize()).value_or(0)880 : m_option_watchpoint.watch_size.GetCurrentValue();881 }882 compiler_type = valobj_sp->GetCompilerType();883 884 // Now it's time to create the watchpoint.885 uint32_t watch_type = 0;886 switch (m_option_watchpoint.watch_type) {887 case OptionGroupWatchpoint::eWatchModify:888 watch_type |= LLDB_WATCH_TYPE_MODIFY;889 break;890 case OptionGroupWatchpoint::eWatchRead:891 watch_type |= LLDB_WATCH_TYPE_READ;892 break;893 case OptionGroupWatchpoint::eWatchReadWrite:894 watch_type |= LLDB_WATCH_TYPE_READ | LLDB_WATCH_TYPE_WRITE;895 break;896 case OptionGroupWatchpoint::eWatchWrite:897 watch_type |= LLDB_WATCH_TYPE_WRITE;898 break;899 case OptionGroupWatchpoint::eWatchInvalid:900 break;901 };902 903 error.Clear();904 WatchpointSP watch_sp =905 target.CreateWatchpoint(addr, size, &compiler_type, watch_type, error);906 if (!watch_sp) {907 result.AppendErrorWithFormat(908 "Watchpoint creation failed (addr=0x%" PRIx64 ", size=%" PRIu64909 ", variable expression='%s').\n",910 addr, static_cast<uint64_t>(size), command.GetArgumentAtIndex(0));911 if (const char *error_message = error.AsCString(nullptr))912 result.AppendError(error_message);913 return;914 }915 916 watch_sp->SetWatchSpec(command.GetArgumentAtIndex(0));917 watch_sp->SetWatchVariable(true);918 if (var_sp) {919 if (var_sp->GetDeclaration().GetFile()) {920 StreamString ss;921 // True to show fullpath for declaration file.922 var_sp->GetDeclaration().DumpStopContext(&ss, true);923 watch_sp->SetDeclInfo(std::string(ss.GetString()));924 }925 if (var_sp->GetScope() == eValueTypeVariableLocal)926 watch_sp->SetupVariableWatchpointDisabler(m_exe_ctx.GetFrameSP());927 }928 output_stream.Printf("Watchpoint created: ");929 watch_sp->GetDescription(&output_stream, lldb::eDescriptionLevelFull);930 output_stream.EOL();931 result.SetStatus(eReturnStatusSuccessFinishResult);932 }933 934private:935 OptionGroupOptions m_option_group;936 OptionGroupWatchpoint m_option_watchpoint;937};938 939// CommandObjectWatchpointSetExpression940#pragma mark Set941 942class CommandObjectWatchpointSetExpression : public CommandObjectRaw {943public:944 CommandObjectWatchpointSetExpression(CommandInterpreter &interpreter)945 : CommandObjectRaw(946 interpreter, "watchpoint set expression",947 "Set a watchpoint on an address by supplying an expression. "948 "Use the '-l' option to specify the language of the expression. "949 "Use the '-w' option to specify the type of watchpoint and "950 "the '-s' option to specify the byte size to watch for. "951 "If no '-w' option is specified, it defaults to modify. "952 "If no '-s' option is specified, it defaults to the target's "953 "pointer byte size. "954 "Note that there are limited hardware resources for watchpoints. "955 "If watchpoint setting fails, consider disable/delete existing "956 "ones "957 "to free up resources.",958 "",959 eCommandRequiresFrame | eCommandTryTargetAPILock |960 eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) {961 SetHelpLong(962 R"(963Examples:964 965(lldb) watchpoint set expression -w modify -s 1 -- foo + 32966 967 Watches write access for the 1-byte region pointed to by the address 'foo + 32')");968 969 AddSimpleArgumentList(eArgTypeExpression);970 971 // Absorb the '-w' and '-s' options into our option group.972 m_option_group.Append(&m_option_watchpoint, LLDB_OPT_SET_ALL,973 LLDB_OPT_SET_1);974 m_option_group.Finalize();975 }976 977 ~CommandObjectWatchpointSetExpression() override = default;978 979 // Overrides base class's behavior where WantsCompletion =980 // !WantsRawCommandString.981 bool WantsCompletion() override { return true; }982 983 Options *GetOptions() override { return &m_option_group; }984 985protected:986 void DoExecute(llvm::StringRef raw_command,987 CommandReturnObject &result) override {988 auto exe_ctx = GetCommandInterpreter().GetExecutionContext();989 m_option_group.NotifyOptionParsingStarting(990 &exe_ctx); // This is a raw command, so notify the option group991 992 Target &target = GetTarget();993 StackFrame *frame = m_exe_ctx.GetFramePtr();994 995 OptionsWithRaw args(raw_command);996 997 llvm::StringRef expr = args.GetRawPart();998 999 if (args.HasArgs())1000 if (!ParseOptionsAndNotify(args.GetArgs(), result, m_option_group,1001 exe_ctx))1002 return;1003 1004 // If no argument is present, issue an error message. There's no way to1005 // set a watchpoint.1006 if (raw_command.trim().empty()) {1007 result.AppendError("required argument missing; specify an expression "1008 "to evaluate into the address to watch for");1009 return;1010 }1011 1012 // If no '-w' is specified, default to '-w write'.1013 if (!m_option_watchpoint.watch_type_specified) {1014 m_option_watchpoint.watch_type = OptionGroupWatchpoint::eWatchModify;1015 }1016 1017 // We passed the sanity check for the command. Proceed to set the1018 // watchpoint now.1019 lldb::addr_t addr = 0;1020 size_t size = 0;1021 1022 ValueObjectSP valobj_sp;1023 1024 // Use expression evaluation to arrive at the address to watch.1025 EvaluateExpressionOptions options;1026 options.SetCoerceToId(false);1027 options.SetUnwindOnError(true);1028 options.SetKeepInMemory(false);1029 options.SetTryAllThreads(true);1030 options.SetTimeout(std::nullopt);1031 if (m_option_watchpoint.language_type != eLanguageTypeUnknown)1032 options.SetLanguage(m_option_watchpoint.language_type);1033 1034 ExpressionResults expr_result =1035 target.EvaluateExpression(expr, frame, valobj_sp, options);1036 if (expr_result != eExpressionCompleted) {1037 result.AppendError("expression evaluation of address to watch failed");1038 result.AppendErrorWithFormat("expression evaluated: \n%s", expr.data());1039 if (valobj_sp && !valobj_sp->GetError().Success())1040 result.AppendError(valobj_sp->GetError().AsCString());1041 return;1042 }1043 1044 // Get the address to watch.1045 bool success = false;1046 addr = valobj_sp->GetValueAsUnsigned(0, &success);1047 if (!success) {1048 result.AppendError("expression did not evaluate to an address");1049 return;1050 }1051 1052 if (m_option_watchpoint.watch_size.GetCurrentValue() != 0)1053 size = m_option_watchpoint.watch_size.GetCurrentValue();1054 else1055 size = target.GetArchitecture().GetAddressByteSize();1056 1057 // Now it's time to create the watchpoint.1058 uint32_t watch_type;1059 switch (m_option_watchpoint.watch_type) {1060 case OptionGroupWatchpoint::eWatchRead:1061 watch_type = LLDB_WATCH_TYPE_READ;1062 break;1063 case OptionGroupWatchpoint::eWatchWrite:1064 watch_type = LLDB_WATCH_TYPE_WRITE;1065 break;1066 case OptionGroupWatchpoint::eWatchModify:1067 watch_type = LLDB_WATCH_TYPE_MODIFY;1068 break;1069 case OptionGroupWatchpoint::eWatchReadWrite:1070 watch_type = LLDB_WATCH_TYPE_READ | LLDB_WATCH_TYPE_WRITE;1071 break;1072 default:1073 watch_type = LLDB_WATCH_TYPE_MODIFY;1074 }1075 1076 // Fetch the type from the value object, the type of the watched object is1077 // the pointee type1078 /// of the expression, so convert to that if we found a valid type.1079 CompilerType compiler_type(valobj_sp->GetCompilerType());1080 1081 std::optional<uint64_t> valobj_size =1082 llvm::expectedToOptional(valobj_sp->GetByteSize());1083 // Set the type as a uint8_t array if the size being watched is1084 // larger than the ValueObject's size (which is probably the size1085 // of a pointer).1086 if (valobj_size && size > *valobj_size) {1087 auto type_system = compiler_type.GetTypeSystem();1088 if (type_system) {1089 CompilerType clang_uint8_type =1090 type_system->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 8);1091 compiler_type = clang_uint8_type.GetArrayType(size);1092 }1093 }1094 1095 Status error;1096 WatchpointSP watch_sp =1097 target.CreateWatchpoint(addr, size, &compiler_type, watch_type, error);1098 if (watch_sp) {1099 watch_sp->SetWatchSpec(std::string(expr));1100 Stream &output_stream = result.GetOutputStream();1101 output_stream.Printf("Watchpoint created: ");1102 watch_sp->GetDescription(&output_stream, lldb::eDescriptionLevelFull);1103 output_stream.EOL();1104 result.SetStatus(eReturnStatusSuccessFinishResult);1105 } else {1106 result.AppendErrorWithFormat("Watchpoint creation failed (addr=0x%" PRIx641107 ", size=%" PRIu64 ").\n",1108 addr, (uint64_t)size);1109 if (error.AsCString(nullptr))1110 result.AppendError(error.AsCString());1111 }1112 }1113 1114private:1115 OptionGroupOptions m_option_group;1116 OptionGroupWatchpoint m_option_watchpoint;1117};1118 1119// CommandObjectWatchpointSet1120#pragma mark Set1121 1122class CommandObjectWatchpointSet : public CommandObjectMultiword {1123public:1124 CommandObjectWatchpointSet(CommandInterpreter &interpreter)1125 : CommandObjectMultiword(1126 interpreter, "watchpoint set", "Commands for setting a watchpoint.",1127 "watchpoint set <subcommand> [<subcommand-options>]") {1128 1129 LoadSubCommand(1130 "variable",1131 CommandObjectSP(new CommandObjectWatchpointSetVariable(interpreter)));1132 LoadSubCommand(1133 "expression",1134 CommandObjectSP(new CommandObjectWatchpointSetExpression(interpreter)));1135 }1136 1137 ~CommandObjectWatchpointSet() override = default;1138};1139 1140// CommandObjectMultiwordWatchpoint1141#pragma mark MultiwordWatchpoint1142 1143CommandObjectMultiwordWatchpoint::CommandObjectMultiwordWatchpoint(1144 CommandInterpreter &interpreter)1145 : CommandObjectMultiword(interpreter, "watchpoint",1146 "Commands for operating on watchpoints.",1147 "watchpoint <subcommand> [<command-options>]") {1148 CommandObjectSP list_command_object(1149 new CommandObjectWatchpointList(interpreter));1150 CommandObjectSP enable_command_object(1151 new CommandObjectWatchpointEnable(interpreter));1152 CommandObjectSP disable_command_object(1153 new CommandObjectWatchpointDisable(interpreter));1154 CommandObjectSP delete_command_object(1155 new CommandObjectWatchpointDelete(interpreter));1156 CommandObjectSP ignore_command_object(1157 new CommandObjectWatchpointIgnore(interpreter));1158 CommandObjectSP command_command_object(1159 new CommandObjectWatchpointCommand(interpreter));1160 CommandObjectSP modify_command_object(1161 new CommandObjectWatchpointModify(interpreter));1162 CommandObjectSP set_command_object(1163 new CommandObjectWatchpointSet(interpreter));1164 1165 list_command_object->SetCommandName("watchpoint list");1166 enable_command_object->SetCommandName("watchpoint enable");1167 disable_command_object->SetCommandName("watchpoint disable");1168 delete_command_object->SetCommandName("watchpoint delete");1169 ignore_command_object->SetCommandName("watchpoint ignore");1170 command_command_object->SetCommandName("watchpoint command");1171 modify_command_object->SetCommandName("watchpoint modify");1172 set_command_object->SetCommandName("watchpoint set");1173 1174 LoadSubCommand("list", list_command_object);1175 LoadSubCommand("enable", enable_command_object);1176 LoadSubCommand("disable", disable_command_object);1177 LoadSubCommand("delete", delete_command_object);1178 LoadSubCommand("ignore", ignore_command_object);1179 LoadSubCommand("command", command_command_object);1180 LoadSubCommand("modify", modify_command_object);1181 LoadSubCommand("set", set_command_object);1182}1183 1184CommandObjectMultiwordWatchpoint::~CommandObjectMultiwordWatchpoint() = default;1185