[lldb/Command] Add --force option for `watchpoint delete` command

Currently, there is no option to delete all the watchpoint without LLDB
asking for a confirmation. Besides making the watchpoint delete command
homogeneous with the breakpoint delete command, this option could also
become handy to trigger automated watchpoint deletion i.e. using
breakpoint actions.

rdar://42560586

Signed-off-by: Med Ismail Bennani <medismail.bennani@gmail.com>
This commit is contained in:
Med Ismail Bennani 2019-12-28 14:47:51 +01:00
parent b63bc648a4
commit 3620e5f28a
3 changed files with 120 additions and 20 deletions

View File

@ -155,6 +155,56 @@ class WatchpointCommandsTestCase(TestBase):
self.expect("process status",
substrs=['exited'])
# Read-write watchpoints not supported on SystemZ
@expectedFailureAll(archs=['s390x'])
def test_rw_watchpoint_delete(self):
"""Test delete watchpoint and expect not to stop for watchpoint."""
self.build(dictionary=self.d)
self.setTearDownCleanup(dictionary=self.d)
exe = self.getBuildArtifact(self.exe_name)
self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET)
# Add a breakpoint to set a watchpoint when stopped on the breakpoint.
lldbutil.run_break_set_by_file_and_line(
self, None, self.line, num_expected_locations=1)
# Run the program.
self.runCmd("run", RUN_SUCCEEDED)
# We should be stopped again due to the breakpoint.
# The stop reason of the thread should be breakpoint.
self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
substrs=['stopped',
'stop reason = breakpoint'])
# Now let's set a read_write-type watchpoint for 'global'.
# There should be two watchpoint hits (see main.c).
self.expect(
"watchpoint set variable -w read_write global",
WATCHPOINT_CREATED,
substrs=[
'Watchpoint created',
'size = 4',
'type = rw',
'%s:%d' %
(self.source,
self.decl)])
# Delete the watchpoint immediately using the force option.
self.expect("watchpoint delete --force",
substrs=['All watchpoints removed.'])
# Use the '-v' option to do verbose listing of the watchpoint.
self.runCmd("watchpoint list -v")
self.runCmd("process continue")
# There should be no more watchpoint hit and the process status should
# be 'exited'.
self.expect("process status",
substrs=['exited'])
# Read-write watchpoints not supported on SystemZ
@expectedFailureAll(archs=['s390x'])
def test_rw_watchpoint_set_ignore_count(self):

View File

@ -414,6 +414,10 @@ protected:
}
};
// CommandObjectWatchpointDelete
#define LLDB_OPTIONS_watchpoint_delete
#include "CommandOptions.inc"
// CommandObjectWatchpointDelete
#pragma mark Delete
@ -423,7 +427,8 @@ public:
: CommandObjectParsed(interpreter, "watchpoint delete",
"Delete the specified watchpoint(s). If no "
"watchpoints are specified, delete them all.",
nullptr, eCommandRequiresTarget) {
nullptr, eCommandRequiresTarget),
m_options() {
CommandArgumentEntry arg;
CommandObject::AddIDsArgumentData(arg, eArgTypeWatchpointID,
eArgTypeWatchpointIDRange);
@ -434,6 +439,41 @@ public:
~CommandObjectWatchpointDelete() override = default;
Options *GetOptions() override { return &m_options; }
class CommandOptions : public Options {
public:
CommandOptions() : Options(), m_force(false) {}
~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::makeArrayRef(g_watchpoint_delete_options);
}
// Instance variables to hold the values for command options.
bool m_force;
};
protected:
bool DoExecute(Args &command, CommandReturnObject &result) override {
Target *target = &GetSelectedTarget();
@ -453,8 +493,9 @@ protected:
return false;
}
if (command.GetArgumentCount() == 0) {
if (!m_interpreter.Confirm(
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...");
@ -465,27 +506,31 @@ protected:
(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();
}
// 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();
}
private:
CommandOptions m_options;
};
// CommandObjectWatchpointIgnore

View File

@ -1126,3 +1126,8 @@ let Command = "watchpoint command add" in {
"to run as command for this watchpoint. Be sure to give a module name if "
"appropriate.">;
}
let Command = "watchpoint delete" in {
def watchpoint_delete_force : Option<"force", "f">, Group<1>,
Desc<"Delete all watchpoints without querying for confirmation.">;
}