| //===-- CommandObjectWatchpoint.cpp -----------------------------*- C++ -*-===// | |
| // | |
| // 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 <vector> | |
| #include "llvm/ADT/StringRef.h" | |
| #include "lldb/Breakpoint/Watchpoint.h" | |
| #include "lldb/Breakpoint/WatchpointList.h" | |
| #include "lldb/Core/ValueObject.h" | |
| #include "lldb/Core/ValueObjectVariable.h" | |
| #include "lldb/Host/OptionParser.h" | |
| #include "lldb/Interpreter/CommandCompletions.h" | |
| #include "lldb/Interpreter/CommandInterpreter.h" | |
| #include "lldb/Interpreter/CommandReturnObject.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" | |
| 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("Thre's no process or it is not alive."); | |
| result.SetStatus(eReturnStatusFailed); | |
| 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.find(RSA[i]) != llvm::StringRef::npos) | |
| 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) { | |
| if (target == nullptr) | |
| return false; | |
| 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), | |
| m_options() { | |
| CommandArgumentEntry arg; | |
| CommandObject::AddIDsArgumentData(arg, eArgTypeWatchpointID, | |
| eArgTypeWatchpointIDRange); | |
| // Add the entry for the first argument for this command to the object's | |
| // arguments vector. | |
| m_arguments.push_back(arg); | |
| } | |
| ~CommandObjectWatchpointList() override = default; | |
| Options *GetOptions() override { return &m_options; } | |
| class CommandOptions : public Options { | |
| public: | |
| CommandOptions() | |
| : Options(), | |
| m_level(lldb::eDescriptionLevelBrief) // Watchpoint List defaults to | |
| // brief descriptions | |
| {} | |
| ~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::makeArrayRef(g_watchpoint_list_options); | |
| } | |
| // Instance variables to hold the values for command options. | |
| lldb::DescriptionLevel m_level; | |
| }; | |
| protected: | |
| bool DoExecute(Args &command, CommandReturnObject &result) override { | |
| Target *target = &GetSelectedTarget(); | |
| if (target->GetProcessSP() && target->GetProcessSP()->IsAlive()) { | |
| uint32_t num_supported_hardware_watchpoints; | |
| Status error = target->GetProcessSP()->GetWatchpointSupportInfo( | |
| num_supported_hardware_watchpoints); | |
| if (error.Success()) | |
| result.AppendMessageWithFormat( | |
| "Number of supported hardware watchpoints: %u\n", | |
| 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 true; | |
| } | |
| 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) { | |
| Watchpoint *wp = watchpoints.GetByIndex(i).get(); | |
| AddWatchpointDescription(&output_stream, wp, 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."); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| const size_t size = wp_ids.size(); | |
| for (size_t i = 0; i < size; ++i) { | |
| Watchpoint *wp = watchpoints.FindByID(wp_ids[i]).get(); | |
| if (wp) | |
| AddWatchpointDescription(&output_stream, wp, m_options.m_level); | |
| result.SetStatus(eReturnStatusSuccessFinishNoResult); | |
| } | |
| } | |
| return result.Succeeded(); | |
| } | |
| 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) { | |
| CommandArgumentEntry arg; | |
| CommandObject::AddIDsArgumentData(arg, eArgTypeWatchpointID, | |
| eArgTypeWatchpointIDRange); | |
| // Add the entry for the first argument for this command to the object's | |
| // arguments vector. | |
| m_arguments.push_back(arg); | |
| } | |
| ~CommandObjectWatchpointEnable() override = default; | |
| protected: | |
| bool DoExecute(Args &command, CommandReturnObject &result) override { | |
| Target *target = &GetSelectedTarget(); | |
| if (!CheckTargetForWatchpointOperations(target, result)) | |
| return false; | |
| 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."); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| if (command.GetArgumentCount() == 0) { | |
| // No watchpoint selected; enable all currently set watchpoints. | |
| target->EnableAllWatchpoints(); | |
| result.AppendMessageWithFormat("All watchpoints enabled. (%" PRIu64 | |
| " watchpoints)\n", | |
| (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."); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| 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.AppendMessageWithFormat("%d watchpoints enabled.\n", count); | |
| result.SetStatus(eReturnStatusSuccessFinishNoResult); | |
| } | |
| return result.Succeeded(); | |
| } | |
| }; | |
| // 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) { | |
| CommandArgumentEntry arg; | |
| CommandObject::AddIDsArgumentData(arg, eArgTypeWatchpointID, | |
| eArgTypeWatchpointIDRange); | |
| // Add the entry for the first argument for this command to the object's | |
| // arguments vector. | |
| m_arguments.push_back(arg); | |
| } | |
| ~CommandObjectWatchpointDisable() override = default; | |
| protected: | |
| bool DoExecute(Args &command, CommandReturnObject &result) override { | |
| Target *target = &GetSelectedTarget(); | |
| if (!CheckTargetForWatchpointOperations(target, result)) | |
| return false; | |
| 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."); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| if (command.GetArgumentCount() == 0) { | |
| // No watchpoint selected; disable all currently set watchpoints. | |
| if (target->DisableAllWatchpoints()) { | |
| result.AppendMessageWithFormat("All watchpoints disabled. (%" PRIu64 | |
| " watchpoints)\n", | |
| (uint64_t)num_watchpoints); | |
| result.SetStatus(eReturnStatusSuccessFinishNoResult); | |
| } else { | |
| result.AppendError("Disable all watchpoints failed\n"); | |
| result.SetStatus(eReturnStatusFailed); | |
| } | |
| } else { | |
| // Particular watchpoints selected; disable them. | |
| std::vector<uint32_t> wp_ids; | |
| if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs( | |
| target, command, wp_ids)) { | |
| result.AppendError("Invalid watchpoints specification."); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| 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.AppendMessageWithFormat("%d watchpoints disabled.\n", count); | |
| result.SetStatus(eReturnStatusSuccessFinishNoResult); | |
| } | |
| return result.Succeeded(); | |
| } | |
| }; | |
| // 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) { | |
| CommandArgumentEntry arg; | |
| CommandObject::AddIDsArgumentData(arg, eArgTypeWatchpointID, | |
| eArgTypeWatchpointIDRange); | |
| // Add the entry for the first argument for this command to the object's | |
| // arguments vector. | |
| m_arguments.push_back(arg); | |
| } | |
| ~CommandObjectWatchpointDelete() override = default; | |
| protected: | |
| bool DoExecute(Args &command, CommandReturnObject &result) override { | |
| Target *target = &GetSelectedTarget(); | |
| if (!CheckTargetForWatchpointOperations(target, result)) | |
| return false; | |
| 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."); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| if (command.GetArgumentCount() == 0) { | |
| if (!m_interpreter.Confirm( | |
| "About to delete all watchpoints, do you want to do that?", | |
| true)) { | |
| result.AppendMessage("Operation cancelled..."); | |
| } else { | |
| target->RemoveAllWatchpoints(); | |
| result.AppendMessageWithFormat("All watchpoints removed. (%" PRIu64 | |
| " watchpoints)\n", | |
| (uint64_t)num_watchpoints); | |
| } | |
| result.SetStatus(eReturnStatusSuccessFinishNoResult); | |
| } else { | |
| // Particular watchpoints selected; delete them. | |
| std::vector<uint32_t> wp_ids; | |
| if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs( | |
| target, command, wp_ids)) { | |
| result.AppendError("Invalid watchpoints specification."); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| 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.AppendMessageWithFormat("%d watchpoints deleted.\n", count); | |
| result.SetStatus(eReturnStatusSuccessFinishNoResult); | |
| } | |
| return result.Succeeded(); | |
| } | |
| }; | |
| // 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), | |
| m_options() { | |
| CommandArgumentEntry arg; | |
| CommandObject::AddIDsArgumentData(arg, eArgTypeWatchpointID, | |
| eArgTypeWatchpointIDRange); | |
| // Add the entry for the first argument for this command to the object's | |
| // arguments vector. | |
| m_arguments.push_back(arg); | |
| } | |
| ~CommandObjectWatchpointIgnore() override = default; | |
| Options *GetOptions() override { return &m_options; } | |
| class CommandOptions : public Options { | |
| public: | |
| CommandOptions() : Options(), m_ignore_count(0) {} | |
| ~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.SetErrorStringWithFormat("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::makeArrayRef(g_watchpoint_ignore_options); | |
| } | |
| // Instance variables to hold the values for command options. | |
| uint32_t m_ignore_count; | |
| }; | |
| protected: | |
| bool DoExecute(Args &command, CommandReturnObject &result) override { | |
| Target *target = &GetSelectedTarget(); | |
| if (!CheckTargetForWatchpointOperations(target, result)) | |
| return false; | |
| 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."); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| if (command.GetArgumentCount() == 0) { | |
| target->IgnoreAllWatchpoints(m_options.m_ignore_count); | |
| result.AppendMessageWithFormat("All watchpoints ignored. (%" PRIu64 | |
| " watchpoints)\n", | |
| (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."); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| 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.AppendMessageWithFormat("%d watchpoints ignored.\n", count); | |
| result.SetStatus(eReturnStatusSuccessFinishNoResult); | |
| } | |
| return result.Succeeded(); | |
| } | |
| 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), | |
| m_options() { | |
| CommandArgumentEntry arg; | |
| CommandObject::AddIDsArgumentData(arg, eArgTypeWatchpointID, | |
| eArgTypeWatchpointIDRange); | |
| // Add the entry for the first argument for this command to the object's | |
| // arguments vector. | |
| m_arguments.push_back(arg); | |
| } | |
| ~CommandObjectWatchpointModify() override = default; | |
| Options *GetOptions() override { return &m_options; } | |
| class CommandOptions : public Options { | |
| public: | |
| CommandOptions() : Options(), m_condition(), m_condition_passed(false) {} | |
| ~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 = 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::makeArrayRef(g_watchpoint_modify_options); | |
| } | |
| // Instance variables to hold the values for command options. | |
| std::string m_condition; | |
| bool m_condition_passed; | |
| }; | |
| protected: | |
| bool DoExecute(Args &command, CommandReturnObject &result) override { | |
| Target *target = &GetSelectedTarget(); | |
| if (!CheckTargetForWatchpointOperations(target, result)) | |
| return false; | |
| 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."); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| if (command.GetArgumentCount() == 0) { | |
| WatchpointSP wp_sp = target->GetLastCreatedWatchpoint(); | |
| wp_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."); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| int count = 0; | |
| const size_t size = wp_ids.size(); | |
| for (size_t i = 0; i < size; ++i) { | |
| WatchpointSP wp_sp = watchpoints.FindByID(wp_ids[i]); | |
| if (wp_sp) { | |
| wp_sp->SetCondition(m_options.m_condition.c_str()); | |
| ++count; | |
| } | |
| } | |
| result.AppendMessageWithFormat("%d watchpoints modified.\n", count); | |
| result.SetStatus(eReturnStatusSuccessFinishNoResult); | |
| } | |
| return result.Succeeded(); | |
| } | |
| 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 write. " | |
| "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), | |
| m_option_group(), m_option_watchpoint() { | |
| 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."); | |
| CommandArgumentEntry arg; | |
| CommandArgumentData var_name_arg; | |
| // Define the only variant of this arg. | |
| var_name_arg.arg_type = eArgTypeVarName; | |
| var_name_arg.arg_repetition = eArgRepeatPlain; | |
| // Push the variant into the argument entry. | |
| arg.push_back(var_name_arg); | |
| // Push the data for the only argument into the m_arguments vector. | |
| m_arguments.push_back(arg); | |
| // 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(); | |
| } | |
| ~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; | |
| } | |
| bool DoExecute(Args &command, CommandReturnObject &result) override { | |
| Target *target = GetDebugger().GetSelectedTarget().get(); | |
| 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.GetErrorStream().Printf("error: required argument missing; " | |
| "specify your program variable to watch " | |
| "for\n"); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| // If no '-w' is specified, default to '-w write'. | |
| if (!m_option_watchpoint.watch_type_specified) { | |
| m_option_watchpoint.watch_type = OptionGroupWatchpoint::eWatchWrite; | |
| } | |
| // We passed the sanity check for the command. Proceed to set the | |
| // watchpoint now. | |
| lldb::addr_t addr = 0; | |
| 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.GetErrorStream().Printf( | |
| "error: specify exactly one variable to watch for\n"); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| // 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) { | |
| AddressType addr_type; | |
| addr = valobj_sp->GetAddressOf(false, &addr_type); | |
| if (addr_type == eAddressTypeLoad) { | |
| // We're in business. | |
| // Find out the size of this variable. | |
| size = m_option_watchpoint.watch_size == 0 | |
| ? valobj_sp->GetByteSize() | |
| : m_option_watchpoint.watch_size; | |
| } | |
| compiler_type = valobj_sp->GetCompilerType(); | |
| } else { | |
| const char *error_cstr = error.AsCString(nullptr); | |
| if (error_cstr) | |
| result.GetErrorStream().Printf("error: %s\n", error_cstr); | |
| else | |
| result.GetErrorStream().Printf("error: unable to find any variable " | |
| "expression path that matches '%s'\n", | |
| command.GetArgumentAtIndex(0)); | |
| return false; | |
| } | |
| // Now it's time to create the watchpoint. | |
| uint32_t watch_type = m_option_watchpoint.watch_type; | |
| error.Clear(); | |
| Watchpoint *wp = | |
| target->CreateWatchpoint(addr, size, &compiler_type, watch_type, error) | |
| .get(); | |
| if (wp) { | |
| wp->SetWatchSpec(command.GetArgumentAtIndex(0)); | |
| wp->SetWatchVariable(true); | |
| if (var_sp && var_sp->GetDeclaration().GetFile()) { | |
| StreamString ss; | |
| // True to show fullpath for declaration file. | |
| var_sp->GetDeclaration().DumpStopContext(&ss, true); | |
| wp->SetDeclInfo(ss.GetString()); | |
| } | |
| output_stream.Printf("Watchpoint created: "); | |
| wp->GetDescription(&output_stream, lldb::eDescriptionLevelFull); | |
| output_stream.EOL(); | |
| result.SetStatus(eReturnStatusSuccessFinishResult); | |
| } else { | |
| result.AppendErrorWithFormat( | |
| "Watchpoint creation failed (addr=0x%" PRIx64 ", size=%" PRIu64 | |
| ", variable expression='%s').\n", | |
| addr, (uint64_t)size, command.GetArgumentAtIndex(0)); | |
| if (error.AsCString(nullptr)) | |
| result.AppendError(error.AsCString()); | |
| result.SetStatus(eReturnStatusFailed); | |
| } | |
| return result.Succeeded(); | |
| } | |
| 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 '-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 write. " | |
| "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), | |
| m_option_group(), m_option_watchpoint() { | |
| SetHelpLong( | |
| R"( | |
| Examples: | |
| (lldb) watchpoint set expression -w write -s 1 -- foo + 32 | |
| Watches write access for the 1-byte region pointed to by the address 'foo + 32')"); | |
| CommandArgumentEntry arg; | |
| CommandArgumentData expression_arg; | |
| // Define the only variant of this arg. | |
| expression_arg.arg_type = eArgTypeExpression; | |
| expression_arg.arg_repetition = eArgRepeatPlain; | |
| // Push the only variant into the argument entry. | |
| arg.push_back(expression_arg); | |
| // Push the data for the only argument into the m_arguments vector. | |
| m_arguments.push_back(arg); | |
| // 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: | |
| bool 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 = GetDebugger().GetSelectedTarget().get(); | |
| 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 false; | |
| // If no argument is present, issue an error message. There's no way to | |
| // set a watchpoint. | |
| if (raw_command.trim().empty()) { | |
| result.GetErrorStream().Printf("error: required argument missing; " | |
| "specify an expression to evaulate into " | |
| "the address to watch for\n"); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| // If no '-w' is specified, default to '-w write'. | |
| if (!m_option_watchpoint.watch_type_specified) { | |
| m_option_watchpoint.watch_type = OptionGroupWatchpoint::eWatchWrite; | |
| } | |
| // 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(llvm::None); | |
| ExpressionResults expr_result = | |
| target->EvaluateExpression(expr, frame, valobj_sp, options); | |
| if (expr_result != eExpressionCompleted) { | |
| result.GetErrorStream().Printf( | |
| "error: expression evaluation of address to watch failed\n"); | |
| result.GetErrorStream() << "expression evaluated: \n" << expr << "\n"; | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| // Get the address to watch. | |
| bool success = false; | |
| addr = valobj_sp->GetValueAsUnsigned(0, &success); | |
| if (!success) { | |
| result.GetErrorStream().Printf( | |
| "error: expression did not evaluate to an address\n"); | |
| result.SetStatus(eReturnStatusFailed); | |
| return false; | |
| } | |
| if (m_option_watchpoint.watch_size != 0) | |
| size = m_option_watchpoint.watch_size; | |
| else | |
| size = target->GetArchitecture().GetAddressByteSize(); | |
| // Now it's time to create the watchpoint. | |
| uint32_t watch_type = m_option_watchpoint.watch_type; | |
| // 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()); | |
| Status error; | |
| Watchpoint *wp = | |
| target->CreateWatchpoint(addr, size, &compiler_type, watch_type, error) | |
| .get(); | |
| if (wp) { | |
| Stream &output_stream = result.GetOutputStream(); | |
| output_stream.Printf("Watchpoint created: "); | |
| wp->GetDescription(&output_stream, lldb::eDescriptionLevelFull); | |
| output_stream.EOL(); | |
| result.SetStatus(eReturnStatusSuccessFinishResult); | |
| } else { | |
| result.AppendErrorWithFormat("Watchpoint creation failed (addr=0x%" PRIx64 | |
| ", size=%" PRIu64 ").\n", | |
| addr, (uint64_t)size); | |
| if (error.AsCString(nullptr)) | |
| result.AppendError(error.AsCString()); | |
| result.SetStatus(eReturnStatusFailed); | |
| } | |
| return result.Succeeded(); | |
| } | |
| 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; |