| //===-- CommandObjectWatchpoint.cpp ---------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "CommandObjectWatchpoint.h" | |
| #include "CommandObjectWatchpointCommand.h" | |
| #include <memory> | |
| #include <vector> | |
| #include "llvm/ADT/StringRef.h" | |
| #include "lldb/Breakpoint/Watchpoint.h" | |
| #include "lldb/Breakpoint/WatchpointList.h" | |
| #include "lldb/Host/OptionParser.h" | |
| #include "lldb/Interpreter/CommandInterpreter.h" | |
| #include "lldb/Interpreter/CommandOptionArgumentTable.h" | |
| #include "lldb/Interpreter/CommandReturnObject.h" | |
| #include "lldb/Symbol/Function.h" | |
| #include "lldb/Symbol/Variable.h" | |
| #include "lldb/Symbol/VariableList.h" | |
| #include "lldb/Target/StackFrame.h" | |
| #include "lldb/Target/Target.h" | |
| #include "lldb/Utility/StreamString.h" | |
| #include "lldb/ValueObject/ValueObject.h" | |
| using namespace lldb; | |
| using namespace lldb_private; | |
| static void AddWatchpointDescription(Stream &s, Watchpoint &wp, | |
| lldb::DescriptionLevel level) { | |
| s.IndentMore(); | |
| wp.GetDescription(&s, level); | |
| s.IndentLess(); | |
| s.EOL(); | |
| } | |
| static bool CheckTargetForWatchpointOperations(Target &target, | |
| CommandReturnObject &result) { | |
| bool process_is_valid = | |
| target.GetProcessSP() && target.GetProcessSP()->IsAlive(); | |
| if (!process_is_valid) { | |
| result.AppendError("there's no process or it is not alive"); | |
| return false; | |
| } | |
| // Target passes our checks, return true. | |
| return true; | |
| } | |
| // Equivalent class: {"-", "to", "To", "TO"} of range specifier array. | |
| static const char *RSA[4] = {"-", "to", "To", "TO"}; | |
| // Return the index to RSA if found; otherwise -1 is returned. | |
| static int32_t WithRSAIndex(llvm::StringRef Arg) { | |
| uint32_t i; | |
| for (i = 0; i < 4; ++i) | |
| if (Arg.contains(RSA[i])) | |
| return i; | |
| return -1; | |
| } | |
| // Return true if wp_ids is successfully populated with the watch ids. False | |
| // otherwise. | |
| bool CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs( | |
| Target &target, Args &args, std::vector<uint32_t> &wp_ids) { | |
| // Pre-condition: args.GetArgumentCount() > 0. | |
| if (args.GetArgumentCount() == 0) { | |
| WatchpointSP watch_sp = target.GetLastCreatedWatchpoint(); | |
| if (watch_sp) { | |
| wp_ids.push_back(watch_sp->GetID()); | |
| return true; | |
| } else | |
| return false; | |
| } | |
| llvm::StringRef Minus("-"); | |
| std::vector<llvm::StringRef> StrRefArgs; | |
| llvm::StringRef first; | |
| llvm::StringRef second; | |
| size_t i; | |
| int32_t idx; | |
| // Go through the arguments and make a canonical form of arg list containing | |
| // only numbers with possible "-" in between. | |
| for (auto &entry : args.entries()) { | |
| if ((idx = WithRSAIndex(entry.ref())) == -1) { | |
| StrRefArgs.push_back(entry.ref()); | |
| continue; | |
| } | |
| // The Arg contains the range specifier, split it, then. | |
| std::tie(first, second) = entry.ref().split(RSA[idx]); | |
| if (!first.empty()) | |
| StrRefArgs.push_back(first); | |
| StrRefArgs.push_back(Minus); | |
| if (!second.empty()) | |
| StrRefArgs.push_back(second); | |
| } | |
| // Now process the canonical list and fill in the vector of uint32_t's. If | |
| // there is any error, return false and the client should ignore wp_ids. | |
| uint32_t beg, end, id; | |
| size_t size = StrRefArgs.size(); | |
| bool in_range = false; | |
| for (i = 0; i < size; ++i) { | |
| llvm::StringRef Arg = StrRefArgs[i]; | |
| if (in_range) { | |
| // Look for the 'end' of the range. Note StringRef::getAsInteger() | |
| // returns true to signify error while parsing. | |
| if (Arg.getAsInteger(0, end)) | |
| return false; | |
| // Found a range! Now append the elements. | |
| for (id = beg; id <= end; ++id) | |
| wp_ids.push_back(id); | |
| in_range = false; | |
| continue; | |
| } | |
| if (i < (size - 1) && StrRefArgs[i + 1] == Minus) { | |
| if (Arg.getAsInteger(0, beg)) | |
| return false; | |
| // Turn on the in_range flag, we are looking for end of range next. | |
| ++i; | |
| in_range = true; | |
| continue; | |
| } | |
| // Otherwise, we have a simple ID. Just append it. | |
| if (Arg.getAsInteger(0, beg)) | |
| return false; | |
| wp_ids.push_back(beg); | |
| } | |
| // It is an error if after the loop, we're still in_range. | |
| return !in_range; | |
| } | |
| // CommandObjectWatchpointList | |
| // CommandObjectWatchpointList::Options | |
| #pragma mark List::CommandOptions | |
| #define LLDB_OPTIONS_watchpoint_list | |
| #include "CommandOptions.inc" | |
| #pragma mark List | |
| class CommandObjectWatchpointList : public CommandObjectParsed { | |
| public: | |
| CommandObjectWatchpointList(CommandInterpreter &interpreter) | |
| : CommandObjectParsed( | |
| interpreter, "watchpoint list", | |
| "List all watchpoints at configurable levels of detail.", nullptr, | |
| eCommandRequiresTarget) { | |
| CommandObject::AddIDsArgumentData(eWatchpointArgs); | |
| } | |
| ~CommandObjectWatchpointList() override = default; | |
| Options *GetOptions() override { return &m_options; } | |
| class CommandOptions : public Options { | |
| public: | |
| CommandOptions() = default; | |
| ~CommandOptions() override = default; | |
| Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, | |
| ExecutionContext *execution_context) override { | |
| Status error; | |
| const int short_option = m_getopt_table[option_idx].val; | |
| switch (short_option) { | |
| case 'b': | |
| m_level = lldb::eDescriptionLevelBrief; | |
| break; | |
| case 'f': | |
| m_level = lldb::eDescriptionLevelFull; | |
| break; | |
| case 'v': | |
| m_level = lldb::eDescriptionLevelVerbose; | |
| break; | |
| default: | |
| llvm_unreachable("Unimplemented option"); | |
| } | |
| return error; | |
| } | |
| void OptionParsingStarting(ExecutionContext *execution_context) override { | |
| m_level = lldb::eDescriptionLevelFull; | |
| } | |
| llvm::ArrayRef<OptionDefinition> GetDefinitions() override { | |
| return llvm::ArrayRef(g_watchpoint_list_options); | |
| } | |
| // Instance variables to hold the values for command options. | |
| lldb::DescriptionLevel m_level = lldb::eDescriptionLevelBrief; | |
| }; | |
| protected: | |
| void DoExecute(Args &command, CommandReturnObject &result) override { | |
| Target *target = GetTarget(); | |
| assert(target && "target guaranteed by eCommandRequiresTarget"); | |
| if (ProcessSP process_sp = target->GetProcessSP()) { | |
| if (process_sp->IsAlive()) { | |
| std::optional<uint32_t> num_supported_hardware_watchpoints = | |
| process_sp->GetWatchpointSlotCount(); | |
| if (num_supported_hardware_watchpoints) | |
| result.AppendMessageWithFormatv( | |
| "Number of supported hardware watchpoints: {0}", | |
| *num_supported_hardware_watchpoints); | |
| } | |
| } | |
| const WatchpointList &watchpoints = target->GetWatchpointList(); | |
| std::unique_lock<std::recursive_mutex> lock; | |
| target->GetWatchpointList().GetListMutex(lock); | |
| size_t num_watchpoints = watchpoints.GetSize(); | |
| if (num_watchpoints == 0) { | |
| result.AppendMessage("No watchpoints currently set."); | |
| result.SetStatus(eReturnStatusSuccessFinishNoResult); | |
| return; | |
| } | |
| Stream &output_stream = result.GetOutputStream(); | |
| if (command.GetArgumentCount() == 0) { | |
| // No watchpoint selected; show info about all currently set watchpoints. | |
| result.AppendMessage("Current watchpoints:"); | |
| for (size_t i = 0; i < num_watchpoints; ++i) { | |
| WatchpointSP watch_sp = watchpoints.GetByIndex(i); | |
| AddWatchpointDescription(output_stream, *watch_sp, m_options.m_level); | |
| } | |
| result.SetStatus(eReturnStatusSuccessFinishNoResult); | |
| } else { | |
| // Particular watchpoints selected; enable them. | |
| std::vector<uint32_t> wp_ids; | |
| if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs( | |
| *target, command, wp_ids)) { | |
| result.AppendError("invalid watchpoints specification"); | |
| return; | |
| } | |
| const size_t size = wp_ids.size(); | |
| for (size_t i = 0; i < size; ++i) { | |
| WatchpointSP watch_sp = watchpoints.FindByID(wp_ids[i]); | |
| if (watch_sp) | |
| AddWatchpointDescription(output_stream, *watch_sp, m_options.m_level); | |
| result.SetStatus(eReturnStatusSuccessFinishNoResult); | |
| } | |
| } | |
| } | |
| private: | |
| CommandOptions m_options; | |
| }; | |
| // CommandObjectWatchpointEnable | |
| #pragma mark Enable | |
| class CommandObjectWatchpointEnable : public CommandObjectParsed { | |
| public: | |
| CommandObjectWatchpointEnable(CommandInterpreter &interpreter) | |
| : CommandObjectParsed(interpreter, "enable", | |
| "Enable the specified disabled watchpoint(s). If " | |
| "no watchpoints are specified, enable all of them.", | |
| nullptr, eCommandRequiresTarget) { | |
| CommandObject::AddIDsArgumentData(eWatchpointArgs); | |
| } | |
| ~CommandObjectWatchpointEnable() override = default; | |
| void | |
| HandleArgumentCompletion(CompletionRequest &request, | |
| OptionElementVector &opt_element_vector) override { | |
| lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks( | |
| GetCommandInterpreter(), lldb::eWatchpointIDCompletion, request, | |
| nullptr); | |
| } | |
| protected: | |
| void DoExecute(Args &command, CommandReturnObject &result) override { | |
| Target *target = GetTarget(); | |
| assert(target && "target guaranteed by eCommandRequiresTarget"); | |
| if (!CheckTargetForWatchpointOperations(*target, result)) | |
| return; | |
| std::unique_lock<std::recursive_mutex> lock; | |
| target->GetWatchpointList().GetListMutex(lock); | |
| const WatchpointList &watchpoints = target->GetWatchpointList(); | |
| size_t num_watchpoints = watchpoints.GetSize(); | |
| if (num_watchpoints == 0) { | |
| result.AppendError("no watchpoints exist to be enabled"); | |
| return; | |
| } | |
| if (command.GetArgumentCount() == 0) { | |
| // No watchpoint selected; enable all currently set watchpoints. | |
| target->EnableAllWatchpoints(); | |
| result.AppendMessageWithFormatv( | |
| "All watchpoints enabled. ({0} watchpoints)", | |
| (uint64_t)num_watchpoints); | |
| result.SetStatus(eReturnStatusSuccessFinishNoResult); | |
| } else { | |
| // Particular watchpoints selected; enable them. | |
| std::vector<uint32_t> wp_ids; | |
| if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs( | |
| *target, command, wp_ids)) { | |
| result.AppendError("invalid watchpoints specification"); | |
| return; | |
| } | |
| int count = 0; | |
| const size_t size = wp_ids.size(); | |
| for (size_t i = 0; i < size; ++i) | |
| if (target->EnableWatchpointByID(wp_ids[i])) | |
| ++count; | |
| result.AppendMessageWithFormatv("{0} watchpoints enabled.", count); | |
| result.SetStatus(eReturnStatusSuccessFinishNoResult); | |
| } | |
| } | |
| }; | |
| // CommandObjectWatchpointDisable | |
| #pragma mark Disable | |
| class CommandObjectWatchpointDisable : public CommandObjectParsed { | |
| public: | |
| CommandObjectWatchpointDisable(CommandInterpreter &interpreter) | |
| : CommandObjectParsed(interpreter, "watchpoint disable", | |
| "Disable the specified watchpoint(s) without " | |
| "removing it/them. If no watchpoints are " | |
| "specified, disable them all.", | |
| nullptr, eCommandRequiresTarget) { | |
| CommandObject::AddIDsArgumentData(eWatchpointArgs); | |
| } | |
| ~CommandObjectWatchpointDisable() override = default; | |
| void | |
| HandleArgumentCompletion(CompletionRequest &request, | |
| OptionElementVector &opt_element_vector) override { | |
| lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks( | |
| GetCommandInterpreter(), lldb::eWatchpointIDCompletion, request, | |
| nullptr); | |
| } | |
| protected: | |
| void DoExecute(Args &command, CommandReturnObject &result) override { | |
| Target *target = GetTarget(); | |
| assert(target && "target guaranteed by eCommandRequiresTarget"); | |
| if (!CheckTargetForWatchpointOperations(*target, result)) | |
| return; | |
| std::unique_lock<std::recursive_mutex> lock; | |
| target->GetWatchpointList().GetListMutex(lock); | |
| const WatchpointList &watchpoints = target->GetWatchpointList(); | |
| size_t num_watchpoints = watchpoints.GetSize(); | |
| if (num_watchpoints == 0) { | |
| result.AppendError("no watchpoints exist to be disabled"); | |
| return; | |
| } | |
| if (command.GetArgumentCount() == 0) { | |
| // No watchpoint selected; disable all currently set watchpoints. | |
| if (target->DisableAllWatchpoints()) { | |
| result.AppendMessageWithFormatv( | |
| "All watchpoints disabled. ({0} watchpoints)", | |
| (uint64_t)num_watchpoints); | |
| result.SetStatus(eReturnStatusSuccessFinishNoResult); | |
| } else { | |
| result.AppendError("Disable all watchpoints failed\n"); | |
| } | |
| } else { | |
| // Particular watchpoints selected; disable them. | |
| std::vector<uint32_t> wp_ids; | |
| if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs( | |
| *target, command, wp_ids)) { | |
| result.AppendError("invalid watchpoints specification"); | |
| return; | |
| } | |
| int count = 0; | |
| const size_t size = wp_ids.size(); | |
| for (size_t i = 0; i < size; ++i) | |
| if (target->DisableWatchpointByID(wp_ids[i])) | |
| ++count; | |
| result.AppendMessageWithFormatv("{0} watchpoints disabled.\n", count); | |
| result.SetStatus(eReturnStatusSuccessFinishNoResult); | |
| } | |
| } | |
| }; | |
| // CommandObjectWatchpointDelete | |
| #define LLDB_OPTIONS_watchpoint_delete | |
| #include "CommandOptions.inc" | |
| // CommandObjectWatchpointDelete | |
| #pragma mark Delete | |
| class CommandObjectWatchpointDelete : public CommandObjectParsed { | |
| public: | |
| CommandObjectWatchpointDelete(CommandInterpreter &interpreter) | |
| : CommandObjectParsed(interpreter, "watchpoint delete", | |
| "Delete the specified watchpoint(s). If no " | |
| "watchpoints are specified, delete them all.", | |
| nullptr, eCommandRequiresTarget) { | |
| CommandObject::AddIDsArgumentData(eWatchpointArgs); | |
| } | |
| ~CommandObjectWatchpointDelete() override = default; | |
| void | |
| HandleArgumentCompletion(CompletionRequest &request, | |
| OptionElementVector &opt_element_vector) override { | |
| lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks( | |
| GetCommandInterpreter(), lldb::eWatchpointIDCompletion, request, | |
| nullptr); | |
| } | |
| Options *GetOptions() override { return &m_options; } | |
| class CommandOptions : public Options { | |
| public: | |
| CommandOptions() = default; | |
| ~CommandOptions() override = default; | |
| Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, | |
| ExecutionContext *execution_context) override { | |
| const int short_option = m_getopt_table[option_idx].val; | |
| switch (short_option) { | |
| case 'f': | |
| m_force = true; | |
| break; | |
| default: | |
| llvm_unreachable("Unimplemented option"); | |
| } | |
| return {}; | |
| } | |
| void OptionParsingStarting(ExecutionContext *execution_context) override { | |
| m_force = false; | |
| } | |
| llvm::ArrayRef<OptionDefinition> GetDefinitions() override { | |
| return llvm::ArrayRef(g_watchpoint_delete_options); | |
| } | |
| // Instance variables to hold the values for command options. | |
| bool m_force = false; | |
| }; | |
| protected: | |
| void DoExecute(Args &command, CommandReturnObject &result) override { | |
| Target *target = GetTarget(); | |
| assert(target && "target guaranteed by eCommandRequiresTarget"); | |
| if (!CheckTargetForWatchpointOperations(*target, result)) | |
| return; | |
| std::unique_lock<std::recursive_mutex> lock; | |
| target->GetWatchpointList().GetListMutex(lock); | |
| const WatchpointList &watchpoints = target->GetWatchpointList(); | |
| size_t num_watchpoints = watchpoints.GetSize(); | |
| if (num_watchpoints == 0) { | |
| result.AppendError("no watchpoints exist to be deleted"); | |
| return; | |
| } | |
| if (command.empty()) { | |
| if (!m_options.m_force && | |
| !m_interpreter.Confirm( | |
| "About to delete all watchpoints, do you want to do that?", | |
| true)) { | |
| result.AppendMessage("Operation cancelled..."); | |
| } else { | |
| target->RemoveAllWatchpoints(); | |
| result.AppendMessageWithFormatv( | |
| "All watchpoints removed. ({0} watchpoints)", | |
| (uint64_t)num_watchpoints); | |
| } | |
| result.SetStatus(eReturnStatusSuccessFinishNoResult); | |
| return; | |
| } | |
| // Particular watchpoints selected; delete them. | |
| std::vector<uint32_t> wp_ids; | |
| if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(*target, command, | |
| wp_ids)) { | |
| result.AppendError("invalid watchpoints specification"); | |
| return; | |
| } | |
| int count = 0; | |
| const size_t size = wp_ids.size(); | |
| for (size_t i = 0; i < size; ++i) | |
| if (target->RemoveWatchpointByID(wp_ids[i])) | |
| ++count; | |
| result.AppendMessageWithFormatv("{0} watchpoints deleted.", count); | |
| result.SetStatus(eReturnStatusSuccessFinishNoResult); | |
| } | |
| private: | |
| CommandOptions m_options; | |
| }; | |
| // CommandObjectWatchpointIgnore | |
| #pragma mark Ignore::CommandOptions | |
| #define LLDB_OPTIONS_watchpoint_ignore | |
| #include "CommandOptions.inc" | |
| class CommandObjectWatchpointIgnore : public CommandObjectParsed { | |
| public: | |
| CommandObjectWatchpointIgnore(CommandInterpreter &interpreter) | |
| : CommandObjectParsed(interpreter, "watchpoint ignore", | |
| "Set ignore count on the specified watchpoint(s). " | |
| "If no watchpoints are specified, set them all.", | |
| nullptr, eCommandRequiresTarget) { | |
| CommandObject::AddIDsArgumentData(eWatchpointArgs); | |
| } | |
| ~CommandObjectWatchpointIgnore() override = default; | |
| void | |
| HandleArgumentCompletion(CompletionRequest &request, | |
| OptionElementVector &opt_element_vector) override { | |
| lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks( | |
| GetCommandInterpreter(), lldb::eWatchpointIDCompletion, request, | |
| nullptr); | |
| } | |
| Options *GetOptions() override { return &m_options; } | |
| class CommandOptions : public Options { | |
| public: | |
| CommandOptions() = default; | |
| ~CommandOptions() override = default; | |
| Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, | |
| ExecutionContext *execution_context) override { | |
| Status error; | |
| const int short_option = m_getopt_table[option_idx].val; | |
| switch (short_option) { | |
| case 'i': | |
| if (option_arg.getAsInteger(0, m_ignore_count)) | |
| error = Status::FromErrorStringWithFormat("invalid ignore count '%s'", | |
| option_arg.str().c_str()); | |
| break; | |
| default: | |
| llvm_unreachable("Unimplemented option"); | |
| } | |
| return error; | |
| } | |
| void OptionParsingStarting(ExecutionContext *execution_context) override { | |
| m_ignore_count = 0; | |
| } | |
| llvm::ArrayRef<OptionDefinition> GetDefinitions() override { | |
| return llvm::ArrayRef(g_watchpoint_ignore_options); | |
| } | |
| // Instance variables to hold the values for command options. | |
| uint32_t m_ignore_count = 0; | |
| }; | |
| protected: | |
| void DoExecute(Args &command, CommandReturnObject &result) override { | |
| Target *target = GetTarget(); | |
| assert(target && "target guaranteed by eCommandRequiresTarget"); | |
| if (!CheckTargetForWatchpointOperations(*target, result)) | |
| return; | |
| std::unique_lock<std::recursive_mutex> lock; | |
| target->GetWatchpointList().GetListMutex(lock); | |
| const WatchpointList &watchpoints = target->GetWatchpointList(); | |
| size_t num_watchpoints = watchpoints.GetSize(); | |
| if (num_watchpoints == 0) { | |
| result.AppendError("no watchpoints exist to be ignored"); | |
| return; | |
| } | |
| if (command.GetArgumentCount() == 0) { | |
| target->IgnoreAllWatchpoints(m_options.m_ignore_count); | |
| result.AppendMessageWithFormatv( | |
| "All watchpoints ignored. ({0} watchpoints)", | |
| (uint64_t)num_watchpoints); | |
| result.SetStatus(eReturnStatusSuccessFinishNoResult); | |
| } else { | |
| // Particular watchpoints selected; ignore them. | |
| std::vector<uint32_t> wp_ids; | |
| if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs( | |
| *target, command, wp_ids)) { | |
| result.AppendError("invalid watchpoints specification"); | |
| return; | |
| } | |
| int count = 0; | |
| const size_t size = wp_ids.size(); | |
| for (size_t i = 0; i < size; ++i) | |
| if (target->IgnoreWatchpointByID(wp_ids[i], m_options.m_ignore_count)) | |
| ++count; | |
| result.AppendMessageWithFormatv("{0} watchpoints ignored.", count); | |
| result.SetStatus(eReturnStatusSuccessFinishNoResult); | |
| } | |
| } | |
| private: | |
| CommandOptions m_options; | |
| }; | |
| // CommandObjectWatchpointModify | |
| #pragma mark Modify::CommandOptions | |
| #define LLDB_OPTIONS_watchpoint_modify | |
| #include "CommandOptions.inc" | |
| #pragma mark Modify | |
| class CommandObjectWatchpointModify : public CommandObjectParsed { | |
| public: | |
| CommandObjectWatchpointModify(CommandInterpreter &interpreter) | |
| : CommandObjectParsed( | |
| interpreter, "watchpoint modify", | |
| "Modify the options on a watchpoint or set of watchpoints in the " | |
| "executable. " | |
| "If no watchpoint is specified, act on the last created " | |
| "watchpoint. " | |
| "Passing an empty argument clears the modification.", | |
| nullptr, eCommandRequiresTarget) { | |
| CommandObject::AddIDsArgumentData(eWatchpointArgs); | |
| } | |
| ~CommandObjectWatchpointModify() override = default; | |
| void | |
| HandleArgumentCompletion(CompletionRequest &request, | |
| OptionElementVector &opt_element_vector) override { | |
| lldb_private::CommandCompletions::InvokeCommonCompletionCallbacks( | |
| GetCommandInterpreter(), lldb::eWatchpointIDCompletion, request, | |
| nullptr); | |
| } | |
| Options *GetOptions() override { return &m_options; } | |
| class CommandOptions : public Options { | |
| public: | |
| CommandOptions() = default; | |
| ~CommandOptions() override = default; | |
| Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, | |
| ExecutionContext *execution_context) override { | |
| Status error; | |
| const int short_option = m_getopt_table[option_idx].val; | |
| switch (short_option) { | |
| case 'c': | |
| m_condition = std::string(option_arg); | |
| m_condition_passed = true; | |
| break; | |
| default: | |
| llvm_unreachable("Unimplemented option"); | |
| } | |
| return error; | |
| } | |
| void OptionParsingStarting(ExecutionContext *execution_context) override { | |
| m_condition.clear(); | |
| m_condition_passed = false; | |
| } | |
| llvm::ArrayRef<OptionDefinition> GetDefinitions() override { | |
| return llvm::ArrayRef(g_watchpoint_modify_options); | |
| } | |
| // Instance variables to hold the values for command options. | |
| std::string m_condition; | |
| bool m_condition_passed = false; | |
| }; | |
| protected: | |
| void DoExecute(Args &command, CommandReturnObject &result) override { | |
| Target *target = GetTarget(); | |
| assert(target && "target guaranteed by eCommandRequiresTarget"); | |
| if (!CheckTargetForWatchpointOperations(*target, result)) | |
| return; | |
| std::unique_lock<std::recursive_mutex> lock; | |
| target->GetWatchpointList().GetListMutex(lock); | |
| const WatchpointList &watchpoints = target->GetWatchpointList(); | |
| size_t num_watchpoints = watchpoints.GetSize(); | |
| if (num_watchpoints == 0) { | |
| result.AppendError("no watchpoints exist to be modified"); | |
| return; | |
| } | |
| if (command.GetArgumentCount() == 0) { | |
| WatchpointSP watch_sp = target->GetLastCreatedWatchpoint(); | |
| watch_sp->SetCondition(m_options.m_condition.c_str()); | |
| result.SetStatus(eReturnStatusSuccessFinishNoResult); | |
| } else { | |
| // Particular watchpoints selected; set condition on them. | |
| std::vector<uint32_t> wp_ids; | |
| if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs( | |
| *target, command, wp_ids)) { | |
| result.AppendError("invalid watchpoints specification"); | |
| return; | |
| } | |
| int count = 0; | |
| const size_t size = wp_ids.size(); | |
| for (size_t i = 0; i < size; ++i) { | |
| WatchpointSP watch_sp = watchpoints.FindByID(wp_ids[i]); | |
| if (watch_sp) { | |
| watch_sp->SetCondition(m_options.m_condition.c_str()); | |
| ++count; | |
| } | |
| } | |
| result.AppendMessageWithFormatv("{0} watchpoints modified.", count); | |
| result.SetStatus(eReturnStatusSuccessFinishNoResult); | |
| } | |
| } | |
| private: | |
| CommandOptions m_options; | |
| }; | |
| // CommandObjectWatchpointSetVariable | |
| #pragma mark SetVariable | |
| class CommandObjectWatchpointSetVariable : public CommandObjectParsed { | |
| public: | |
| CommandObjectWatchpointSetVariable(CommandInterpreter &interpreter) | |
| : CommandObjectParsed( | |
| interpreter, "watchpoint set variable", | |
| "Set a watchpoint on a variable. " | |
| "Use the '-w' option to specify the type of watchpoint and " | |
| "the '-s' option to specify the byte size to watch for. " | |
| "If no '-w' option is specified, it defaults to modify. " | |
| "If no '-s' option is specified, it defaults to the variable's " | |
| "byte size. " | |
| "Note that there are limited hardware resources for watchpoints. " | |
| "If watchpoint setting fails, consider disable/delete existing " | |
| "ones " | |
| "to free up resources.", | |
| nullptr, | |
| eCommandRequiresFrame | eCommandTryTargetAPILock | | |
| eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) { | |
| SetHelpLong( | |
| R"( | |
| Examples: | |
| (lldb) watchpoint set variable -w read_write my_global_var | |
| )" | |
| " Watches my_global_var for read/write access, with the region to watch \ | |
| corresponding to the byte size of the data type."); | |
| AddSimpleArgumentList(eArgTypeVarName); | |
| // Absorb the '-w' and '-s' options into our option group. | |
| m_option_group.Append(&m_option_watchpoint, LLDB_OPT_SET_1, LLDB_OPT_SET_1); | |
| m_option_group.Finalize(); | |
| } | |
| ~CommandObjectWatchpointSetVariable() override = default; | |
| Options *GetOptions() override { return &m_option_group; } | |
| protected: | |
| static size_t GetVariableCallback(void *baton, const char *name, | |
| VariableList &variable_list) { | |
| size_t old_size = variable_list.GetSize(); | |
| Target *target = static_cast<Target *>(baton); | |
| if (target) | |
| target->GetImages().FindGlobalVariables(ConstString(name), UINT32_MAX, | |
| variable_list); | |
| return variable_list.GetSize() - old_size; | |
| } | |
| void DoExecute(Args &command, CommandReturnObject &result) override { | |
| Target *target = GetTarget(); | |
| assert(target && "target guaranteed by eCommandRequiresTarget"); | |
| StackFrame *frame = m_exe_ctx.GetFramePtr(); | |
| // If no argument is present, issue an error message. There's no way to | |
| // set a watchpoint. | |
| if (command.GetArgumentCount() <= 0) { | |
| result.AppendError("required argument missing; " | |
| "specify your program variable to watch for"); | |
| return; | |
| } | |
| // If no '-w' is specified, default to '-w modify'. | |
| if (!m_option_watchpoint.watch_type_specified) { | |
| m_option_watchpoint.watch_type = OptionGroupWatchpoint::eWatchModify; | |
| } | |
| // We passed the sanity check for the command. Proceed to set the | |
| // watchpoint now. | |
| size_t size = 0; | |
| VariableSP var_sp; | |
| ValueObjectSP valobj_sp; | |
| Stream &output_stream = result.GetOutputStream(); | |
| // A simple watch variable gesture allows only one argument. | |
| if (command.GetArgumentCount() != 1) { | |
| result.AppendError("specify exactly one variable to watch for"); | |
| return; | |
| } | |
| // Things have checked out ok... | |
| Status error; | |
| uint32_t expr_path_options = | |
| StackFrame::eExpressionPathOptionCheckPtrVsMember | | |
| StackFrame::eExpressionPathOptionsAllowDirectIVarAccess; | |
| valobj_sp = frame->GetValueForVariableExpressionPath( | |
| command.GetArgumentAtIndex(0), eNoDynamicValues, expr_path_options, | |
| var_sp, error); | |
| if (!valobj_sp) { | |
| // Not in the frame; let's check the globals. | |
| VariableList variable_list; | |
| ValueObjectList valobj_list; | |
| Status error(Variable::GetValuesForVariableExpressionPath( | |
| command.GetArgumentAtIndex(0), | |
| m_exe_ctx.GetBestExecutionContextScope(), GetVariableCallback, target, | |
| variable_list, valobj_list)); | |
| if (valobj_list.GetSize()) | |
| valobj_sp = valobj_list.GetValueObjectAtIndex(0); | |
| } | |
| CompilerType compiler_type; | |
| if (!valobj_sp) { | |
| const char *error_cstr = error.AsCString(nullptr); | |
| if (error_cstr) | |
| result.AppendError(error_cstr); | |
| else | |
| result.AppendErrorWithFormat("unable to find any variable " | |
| "expression path that matches '%s'", | |
| command.GetArgumentAtIndex(0)); | |
| return; | |
| } | |
| auto [addr, addr_type] = valobj_sp->GetAddressOf(false); | |
| if (addr_type == eAddressTypeLoad) { | |
| // We're in business. | |
| // Find out the size of this variable. | |
| size = | |
| m_option_watchpoint.watch_size.GetCurrentValue() == 0 | |
| ? llvm::expectedToOptional(valobj_sp->GetByteSize()).value_or(0) | |
| : m_option_watchpoint.watch_size.GetCurrentValue(); | |
| } | |
| compiler_type = valobj_sp->GetCompilerType(); | |
| // Now it's time to create the watchpoint. | |
| uint32_t watch_type = 0; | |
| switch (m_option_watchpoint.watch_type) { | |
| case OptionGroupWatchpoint::eWatchModify: | |
| watch_type |= LLDB_WATCH_TYPE_MODIFY; | |
| break; | |
| case OptionGroupWatchpoint::eWatchRead: | |
| watch_type |= LLDB_WATCH_TYPE_READ; | |
| break; | |
| case OptionGroupWatchpoint::eWatchReadWrite: | |
| watch_type |= LLDB_WATCH_TYPE_READ | LLDB_WATCH_TYPE_WRITE; | |
| break; | |
| case OptionGroupWatchpoint::eWatchWrite: | |
| watch_type |= LLDB_WATCH_TYPE_WRITE; | |
| break; | |
| case OptionGroupWatchpoint::eWatchInvalid: | |
| break; | |
| }; | |
| error.Clear(); | |
| WatchpointSP watch_sp = | |
| target->CreateWatchpoint(addr, size, &compiler_type, watch_type, error); | |
| if (!watch_sp) { | |
| result.AppendErrorWithFormat( | |
| "Watchpoint creation failed (addr=0x%" PRIx64 ", size=%" PRIu64 | |
| ", variable expression='%s')", | |
| addr, static_cast<uint64_t>(size), command.GetArgumentAtIndex(0)); | |
| if (const char *error_message = error.AsCString(nullptr)) | |
| result.AppendError(error_message); | |
| return; | |
| } | |
| watch_sp->SetWatchSpec(command.GetArgumentAtIndex(0)); | |
| watch_sp->SetWatchVariable(true); | |
| if (var_sp) { | |
| if (var_sp->GetDeclaration().GetFile()) { | |
| StreamString ss; | |
| // True to show fullpath for declaration file. | |
| var_sp->GetDeclaration().DumpStopContext(&ss, true); | |
| watch_sp->SetDeclInfo(std::string(ss.GetString())); | |
| } | |
| if (var_sp->GetScope() == eValueTypeVariableLocal) | |
| watch_sp->SetupVariableWatchpointDisabler(m_exe_ctx.GetFrameSP()); | |
| } | |
| output_stream.PutCString("Watchpoint created: "); | |
| watch_sp->GetDescription(&output_stream, lldb::eDescriptionLevelFull); | |
| output_stream.EOL(); | |
| result.SetStatus(eReturnStatusSuccessFinishResult); | |
| } | |
| private: | |
| OptionGroupOptions m_option_group; | |
| OptionGroupWatchpoint m_option_watchpoint; | |
| }; | |
| // CommandObjectWatchpointSetExpression | |
| #pragma mark Set | |
| class CommandObjectWatchpointSetExpression : public CommandObjectRaw { | |
| public: | |
| CommandObjectWatchpointSetExpression(CommandInterpreter &interpreter) | |
| : CommandObjectRaw( | |
| interpreter, "watchpoint set expression", | |
| "Set a watchpoint on an address by supplying an expression. " | |
| "Use the '-l' option to specify the language of the expression. " | |
| "Use the '-w' option to specify the type of watchpoint and " | |
| "the '-s' option to specify the byte size to watch for. " | |
| "If no '-w' option is specified, it defaults to modify. " | |
| "If no '-s' option is specified, it defaults to the target's " | |
| "pointer byte size. " | |
| "Note that there are limited hardware resources for watchpoints. " | |
| "If watchpoint setting fails, consider disable/delete existing " | |
| "ones " | |
| "to free up resources.", | |
| "", | |
| eCommandRequiresFrame | eCommandTryTargetAPILock | | |
| eCommandProcessMustBeLaunched | eCommandProcessMustBePaused) { | |
| SetHelpLong( | |
| R"( | |
| Examples: | |
| (lldb) watchpoint set expression -w modify -s 1 -- foo + 32 | |
| Watches write access for the 1-byte region pointed to by the address 'foo + 32')"); | |
| AddSimpleArgumentList(eArgTypeExpression); | |
| // Absorb the '-w' and '-s' options into our option group. | |
| m_option_group.Append(&m_option_watchpoint, LLDB_OPT_SET_ALL, | |
| LLDB_OPT_SET_1); | |
| m_option_group.Finalize(); | |
| } | |
| ~CommandObjectWatchpointSetExpression() override = default; | |
| // Overrides base class's behavior where WantsCompletion = | |
| // !WantsRawCommandString. | |
| bool WantsCompletion() override { return true; } | |
| Options *GetOptions() override { return &m_option_group; } | |
| protected: | |
| void DoExecute(llvm::StringRef raw_command, | |
| CommandReturnObject &result) override { | |
| auto exe_ctx = GetCommandInterpreter().GetExecutionContext(); | |
| m_option_group.NotifyOptionParsingStarting( | |
| &exe_ctx); // This is a raw command, so notify the option group | |
| Target *target = GetTarget(); | |
| assert(target && "target guaranteed by eCommandRequiresTarget"); | |
| StackFrame *frame = m_exe_ctx.GetFramePtr(); | |
| OptionsWithRaw args(raw_command); | |
| llvm::StringRef expr = args.GetRawPart(); | |
| if (args.HasArgs()) | |
| if (!ParseOptionsAndNotify(args.GetArgs(), result, m_option_group, | |
| exe_ctx)) | |
| return; | |
| // If no argument is present, issue an error message. There's no way to | |
| // set a watchpoint. | |
| if (raw_command.trim().empty()) { | |
| result.AppendError("required argument missing; specify an expression " | |
| "to evaluate into the address to watch for"); | |
| return; | |
| } | |
| // If no '-w' is specified, default to '-w write'. | |
| if (!m_option_watchpoint.watch_type_specified) { | |
| m_option_watchpoint.watch_type = OptionGroupWatchpoint::eWatchModify; | |
| } | |
| // We passed the sanity check for the command. Proceed to set the | |
| // watchpoint now. | |
| lldb::addr_t addr = 0; | |
| size_t size = 0; | |
| ValueObjectSP valobj_sp; | |
| // Use expression evaluation to arrive at the address to watch. | |
| EvaluateExpressionOptions options; | |
| options.SetCoerceToId(false); | |
| options.SetUnwindOnError(true); | |
| options.SetKeepInMemory(false); | |
| options.SetTryAllThreads(true); | |
| options.SetTimeout(std::nullopt); | |
| if (m_option_watchpoint.language_type != eLanguageTypeUnknown) | |
| options.SetLanguage(m_option_watchpoint.language_type); | |
| ExpressionResults expr_result = | |
| target->EvaluateExpression(expr, frame, valobj_sp, options); | |
| if (expr_result != eExpressionCompleted) { | |
| result.AppendError("expression evaluation of address to watch failed"); | |
| result.AppendErrorWithFormat("expression evaluated: \n%s", expr.data()); | |
| if (valobj_sp && !valobj_sp->GetError().Success()) | |
| result.AppendError(valobj_sp->GetError().AsCString()); | |
| return; | |
| } | |
| // Get the address to watch. | |
| bool success = false; | |
| addr = valobj_sp->GetValueAsUnsigned(0, &success); | |
| if (!success) { | |
| result.AppendError("expression did not evaluate to an address"); | |
| return; | |
| } | |
| if (m_option_watchpoint.watch_size.GetCurrentValue() != 0) | |
| size = m_option_watchpoint.watch_size.GetCurrentValue(); | |
| else | |
| size = target->GetArchitecture().GetAddressByteSize(); | |
| // Now it's time to create the watchpoint. | |
| uint32_t watch_type; | |
| switch (m_option_watchpoint.watch_type) { | |
| case OptionGroupWatchpoint::eWatchRead: | |
| watch_type = LLDB_WATCH_TYPE_READ; | |
| break; | |
| case OptionGroupWatchpoint::eWatchWrite: | |
| watch_type = LLDB_WATCH_TYPE_WRITE; | |
| break; | |
| case OptionGroupWatchpoint::eWatchModify: | |
| watch_type = LLDB_WATCH_TYPE_MODIFY; | |
| break; | |
| case OptionGroupWatchpoint::eWatchReadWrite: | |
| watch_type = LLDB_WATCH_TYPE_READ | LLDB_WATCH_TYPE_WRITE; | |
| break; | |
| default: | |
| watch_type = LLDB_WATCH_TYPE_MODIFY; | |
| } | |
| // Fetch the type from the value object, the type of the watched object is | |
| // the pointee type | |
| /// of the expression, so convert to that if we found a valid type. | |
| CompilerType compiler_type(valobj_sp->GetCompilerType()); | |
| std::optional<uint64_t> valobj_size = | |
| llvm::expectedToOptional(valobj_sp->GetByteSize()); | |
| // Set the type as a uint8_t array if the size being watched is | |
| // larger than the ValueObject's size (which is probably the size | |
| // of a pointer). | |
| if (valobj_size && size > *valobj_size) { | |
| auto type_system = compiler_type.GetTypeSystem(); | |
| if (type_system) { | |
| CompilerType clang_uint8_type = | |
| type_system->GetBuiltinTypeForEncodingAndBitSize(eEncodingUint, 8); | |
| compiler_type = clang_uint8_type.GetArrayType(size); | |
| } | |
| } | |
| Status error; | |
| WatchpointSP watch_sp = | |
| target->CreateWatchpoint(addr, size, &compiler_type, watch_type, error); | |
| if (watch_sp) { | |
| watch_sp->SetWatchSpec(std::string(expr)); | |
| Stream &output_stream = result.GetOutputStream(); | |
| output_stream.PutCString("Watchpoint created: "); | |
| watch_sp->GetDescription(&output_stream, lldb::eDescriptionLevelFull); | |
| output_stream.EOL(); | |
| result.SetStatus(eReturnStatusSuccessFinishResult); | |
| } else { | |
| result.AppendErrorWithFormat("Watchpoint creation failed (addr=0x%" PRIx64 | |
| ", size=%" PRIu64 ")", | |
| addr, (uint64_t)size); | |
| if (error.AsCString(nullptr)) | |
| result.AppendError(error.AsCString()); | |
| } | |
| } | |
| private: | |
| OptionGroupOptions m_option_group; | |
| OptionGroupWatchpoint m_option_watchpoint; | |
| }; | |
| // CommandObjectWatchpointSet | |
| #pragma mark Set | |
| class CommandObjectWatchpointSet : public CommandObjectMultiword { | |
| public: | |
| CommandObjectWatchpointSet(CommandInterpreter &interpreter) | |
| : CommandObjectMultiword( | |
| interpreter, "watchpoint set", "Commands for setting a watchpoint.", | |
| "watchpoint set <subcommand> [<subcommand-options>]") { | |
| LoadSubCommand( | |
| "variable", | |
| CommandObjectSP(new CommandObjectWatchpointSetVariable(interpreter))); | |
| LoadSubCommand( | |
| "expression", | |
| CommandObjectSP(new CommandObjectWatchpointSetExpression(interpreter))); | |
| } | |
| ~CommandObjectWatchpointSet() override = default; | |
| }; | |
| // CommandObjectMultiwordWatchpoint | |
| #pragma mark MultiwordWatchpoint | |
| CommandObjectMultiwordWatchpoint::CommandObjectMultiwordWatchpoint( | |
| CommandInterpreter &interpreter) | |
| : CommandObjectMultiword(interpreter, "watchpoint", | |
| "Commands for operating on watchpoints.", | |
| "watchpoint <subcommand> [<command-options>]") { | |
| CommandObjectSP list_command_object( | |
| new CommandObjectWatchpointList(interpreter)); | |
| CommandObjectSP enable_command_object( | |
| new CommandObjectWatchpointEnable(interpreter)); | |
| CommandObjectSP disable_command_object( | |
| new CommandObjectWatchpointDisable(interpreter)); | |
| CommandObjectSP delete_command_object( | |
| new CommandObjectWatchpointDelete(interpreter)); | |
| CommandObjectSP ignore_command_object( | |
| new CommandObjectWatchpointIgnore(interpreter)); | |
| CommandObjectSP command_command_object( | |
| new CommandObjectWatchpointCommand(interpreter)); | |
| CommandObjectSP modify_command_object( | |
| new CommandObjectWatchpointModify(interpreter)); | |
| CommandObjectSP set_command_object( | |
| new CommandObjectWatchpointSet(interpreter)); | |
| list_command_object->SetCommandName("watchpoint list"); | |
| enable_command_object->SetCommandName("watchpoint enable"); | |
| disable_command_object->SetCommandName("watchpoint disable"); | |
| delete_command_object->SetCommandName("watchpoint delete"); | |
| ignore_command_object->SetCommandName("watchpoint ignore"); | |
| command_command_object->SetCommandName("watchpoint command"); | |
| modify_command_object->SetCommandName("watchpoint modify"); | |
| set_command_object->SetCommandName("watchpoint set"); | |
| LoadSubCommand("list", list_command_object); | |
| LoadSubCommand("enable", enable_command_object); | |
| LoadSubCommand("disable", disable_command_object); | |
| LoadSubCommand("delete", delete_command_object); | |
| LoadSubCommand("ignore", ignore_command_object); | |
| LoadSubCommand("command", command_command_object); | |
| LoadSubCommand("modify", modify_command_object); | |
| LoadSubCommand("set", set_command_object); | |
| } | |
| CommandObjectMultiwordWatchpoint::~CommandObjectMultiwordWatchpoint() = default; |