1 //===-- CommandObjectWatchpointCommand.cpp ----------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 // C Includes
11 // C++ Includes
12 #include <vector>
13 
14 // Other libraries and framework includes
15 // Project includes
16 #include "CommandObjectWatchpoint.h"
17 #include "CommandObjectWatchpointCommand.h"
18 #include "lldb/Breakpoint/StoppointCallbackContext.h"
19 #include "lldb/Breakpoint/Watchpoint.h"
20 #include "lldb/Core/IOHandler.h"
21 #include "lldb/Core/State.h"
22 #include "lldb/Interpreter/CommandInterpreter.h"
23 #include "lldb/Interpreter/CommandReturnObject.h"
24 #include "lldb/Target/Target.h"
25 #include "lldb/Target/Thread.h"
26 
27 using namespace lldb;
28 using namespace lldb_private;
29 
30 //-------------------------------------------------------------------------
31 // CommandObjectWatchpointCommandAdd
32 //-------------------------------------------------------------------------
33 
34 class CommandObjectWatchpointCommandAdd : public CommandObjectParsed,
35                                           public IOHandlerDelegateMultiline {
36 public:
37   CommandObjectWatchpointCommandAdd(CommandInterpreter &interpreter)
38       : CommandObjectParsed(interpreter, "add",
39                             "Add a set of LLDB commands to a watchpoint, to be "
40                             "executed whenever the watchpoint is hit.",
41                             nullptr),
42         IOHandlerDelegateMultiline("DONE",
43                                    IOHandlerDelegate::Completion::LLDBCommand),
44         m_options() {
45     SetHelpLong(
46         R"(
47 General information about entering watchpoint commands
48 ------------------------------------------------------
49 
50 )"
51         "This command will prompt for commands to be executed when the specified \
52 watchpoint is hit.  Each command is typed on its own line following the '> ' \
53 prompt until 'DONE' is entered."
54         R"(
55 
56 )"
57         "Syntactic errors may not be detected when initially entered, and many \
58 malformed commands can silently fail when executed.  If your watchpoint commands \
59 do not appear to be executing, double-check the command syntax."
60         R"(
61 
62 )"
63         "Note: You may enter any debugger command exactly as you would at the debugger \
64 prompt.  There is no limit to the number of commands supplied, but do NOT enter \
65 more than one command per line."
66         R"(
67 
68 Special information about PYTHON watchpoint commands
69 ----------------------------------------------------
70 
71 )"
72         "You may enter either one or more lines of Python, including function \
73 definitions or calls to functions that will have been imported by the time \
74 the code executes.  Single line watchpoint commands will be interpreted 'as is' \
75 when the watchpoint is hit.  Multiple lines of Python will be wrapped in a \
76 generated function, and a call to the function will be attached to the watchpoint."
77         R"(
78 
79 This auto-generated function is passed in three arguments:
80 
81     frame:  an lldb.SBFrame object for the frame which hit the watchpoint.
82 
83     wp:     the watchpoint that was hit.
84 
85 )"
86         "When specifying a python function with the --python-function option, you need \
87 to supply the function name prepended by the module name:"
88         R"(
89 
90     --python-function myutils.watchpoint_callback
91 
92 The function itself must have the following prototype:
93 
94 def watchpoint_callback(frame, wp):
95   # Your code goes here
96 
97 )"
98         "The arguments are the same as the arguments passed to generated functions as \
99 described above.  Note that the global variable 'lldb.frame' will NOT be updated when \
100 this function is called, so be sure to use the 'frame' argument. The 'frame' argument \
101 can get you to the thread via frame.GetThread(), the thread can get you to the \
102 process via thread.GetProcess(), and the process can get you back to the target \
103 via process.GetTarget()."
104         R"(
105 
106 )"
107         "Important Note: As Python code gets collected into functions, access to global \
108 variables requires explicit scoping using the 'global' keyword.  Be sure to use correct \
109 Python syntax, including indentation, when entering Python watchpoint commands."
110         R"(
111 
112 Example Python one-line watchpoint command:
113 
114 (lldb) watchpoint command add -s python 1
115 Enter your Python command(s). Type 'DONE' to end.
116 > print "Hit this watchpoint!"
117 > DONE
118 
119 As a convenience, this also works for a short Python one-liner:
120 
121 (lldb) watchpoint command add -s python 1 -o 'import time; print time.asctime()'
122 (lldb) run
123 Launching '.../a.out'  (x86_64)
124 (lldb) Fri Sep 10 12:17:45 2010
125 Process 21778 Stopped
126 * thread #1: tid = 0x2e03, 0x0000000100000de8 a.out`c + 7 at main.c:39, stop reason = watchpoint 1.1, queue = com.apple.main-thread
127   36
128   37   	int c(int val)
129   38   	{
130   39 ->	    return val + 3;
131   40   	}
132   41
133   42   	int main (int argc, char const *argv[])
134 
135 Example multiple line Python watchpoint command, using function definition:
136 
137 (lldb) watchpoint command add -s python 1
138 Enter your Python command(s). Type 'DONE' to end.
139 > def watchpoint_output (wp_no):
140 >     out_string = "Hit watchpoint number " + repr (wp_no)
141 >     print out_string
142 >     return True
143 > watchpoint_output (1)
144 > DONE
145 
146 Example multiple line Python watchpoint command, using 'loose' Python:
147 
148 (lldb) watchpoint command add -s p 1
149 Enter your Python command(s). Type 'DONE' to end.
150 > global wp_count
151 > wp_count = wp_count + 1
152 > print "Hit this watchpoint " + repr(wp_count) + " times!"
153 > DONE
154 
155 )"
156         "In this case, since there is a reference to a global variable, \
157 'wp_count', you will also need to make sure 'wp_count' exists and is \
158 initialized:"
159         R"(
160 
161 (lldb) script
162 >>> wp_count = 0
163 >>> quit()
164 
165 )"
166         "Final Note: A warning that no watchpoint command was generated when there \
167 are no syntax errors may indicate that a function was declared but never called.");
168 
169     CommandArgumentEntry arg;
170     CommandArgumentData wp_id_arg;
171 
172     // Define the first (and only) variant of this arg.
173     wp_id_arg.arg_type = eArgTypeWatchpointID;
174     wp_id_arg.arg_repetition = eArgRepeatPlain;
175 
176     // There is only one variant this argument could be; put it into the
177     // argument entry.
178     arg.push_back(wp_id_arg);
179 
180     // Push the data for the first argument into the m_arguments vector.
181     m_arguments.push_back(arg);
182   }
183 
184   ~CommandObjectWatchpointCommandAdd() override = default;
185 
186   Options *GetOptions() override { return &m_options; }
187 
188   void IOHandlerActivated(IOHandler &io_handler) override {
189     StreamFileSP output_sp(io_handler.GetOutputStreamFile());
190     if (output_sp) {
191       output_sp->PutCString(
192           "Enter your debugger command(s).  Type 'DONE' to end.\n");
193       output_sp->Flush();
194     }
195   }
196 
197   void IOHandlerInputComplete(IOHandler &io_handler,
198                               std::string &line) override {
199     io_handler.SetIsDone(true);
200 
201     // The WatchpointOptions object is owned by the watchpoint or watchpoint
202     // location
203     WatchpointOptions *wp_options =
204         (WatchpointOptions *)io_handler.GetUserData();
205     if (wp_options) {
206       std::unique_ptr<WatchpointOptions::CommandData> data_ap(
207           new WatchpointOptions::CommandData());
208       if (data_ap) {
209         data_ap->user_source.SplitIntoLines(line);
210         BatonSP baton_sp(
211             new WatchpointOptions::CommandBaton(data_ap.release()));
212         wp_options->SetCallback(WatchpointOptionsCallbackFunction, baton_sp);
213       }
214     }
215   }
216 
217   void CollectDataForWatchpointCommandCallback(WatchpointOptions *wp_options,
218                                                CommandReturnObject &result) {
219     m_interpreter.GetLLDBCommandsFromIOHandler(
220         "> ",        // Prompt
221         *this,       // IOHandlerDelegate
222         true,        // Run IOHandler in async mode
223         wp_options); // Baton for the "io_handler" that will be passed back into
224                      // our IOHandlerDelegate functions
225   }
226 
227   /// Set a one-liner as the callback for the watchpoint.
228   void SetWatchpointCommandCallback(WatchpointOptions *wp_options,
229                                     const char *oneliner) {
230     std::unique_ptr<WatchpointOptions::CommandData> data_ap(
231         new WatchpointOptions::CommandData());
232 
233     // It's necessary to set both user_source and script_source to the oneliner.
234     // The former is used to generate callback description (as in watchpoint
235     // command list)
236     // while the latter is used for Python to interpret during the actual
237     // callback.
238     data_ap->user_source.AppendString(oneliner);
239     data_ap->script_source.assign(oneliner);
240     data_ap->stop_on_error = m_options.m_stop_on_error;
241 
242     BatonSP baton_sp(new WatchpointOptions::CommandBaton(data_ap.release()));
243     wp_options->SetCallback(WatchpointOptionsCallbackFunction, baton_sp);
244   }
245 
246   static bool
247   WatchpointOptionsCallbackFunction(void *baton,
248                                     StoppointCallbackContext *context,
249                                     lldb::user_id_t watch_id) {
250     bool ret_value = true;
251     if (baton == nullptr)
252       return true;
253 
254     WatchpointOptions::CommandData *data =
255         (WatchpointOptions::CommandData *)baton;
256     StringList &commands = data->user_source;
257 
258     if (commands.GetSize() > 0) {
259       ExecutionContext exe_ctx(context->exe_ctx_ref);
260       Target *target = exe_ctx.GetTargetPtr();
261       if (target) {
262         CommandReturnObject result;
263         Debugger &debugger = target->GetDebugger();
264         // Rig up the results secondary output stream to the debugger's, so the
265         // output will come out synchronously
266         // if the debugger is set up that way.
267 
268         StreamSP output_stream(debugger.GetAsyncOutputStream());
269         StreamSP error_stream(debugger.GetAsyncErrorStream());
270         result.SetImmediateOutputStream(output_stream);
271         result.SetImmediateErrorStream(error_stream);
272 
273         CommandInterpreterRunOptions options;
274         options.SetStopOnContinue(true);
275         options.SetStopOnError(data->stop_on_error);
276         options.SetEchoCommands(false);
277         options.SetPrintResults(true);
278         options.SetAddToHistory(false);
279 
280         debugger.GetCommandInterpreter().HandleCommands(commands, &exe_ctx,
281                                                         options, result);
282         result.GetImmediateOutputStream()->Flush();
283         result.GetImmediateErrorStream()->Flush();
284       }
285     }
286     return ret_value;
287   }
288 
289   class CommandOptions : public Options {
290   public:
291     CommandOptions()
292         : Options(), m_use_commands(false), m_use_script_language(false),
293           m_script_language(eScriptLanguageNone), m_use_one_liner(false),
294           m_one_liner(), m_function_name() {}
295 
296     ~CommandOptions() override = default;
297 
298     Error SetOptionValue(uint32_t option_idx, const char *option_arg,
299                          ExecutionContext *execution_context) override {
300       Error error;
301       const int short_option = m_getopt_table[option_idx].val;
302 
303       switch (short_option) {
304       case 'o':
305         m_use_one_liner = true;
306         m_one_liner = option_arg;
307         break;
308 
309       case 's':
310         m_script_language = (lldb::ScriptLanguage)Args::StringToOptionEnum(
311             option_arg, g_option_table[option_idx].enum_values,
312             eScriptLanguageNone, error);
313 
314         m_use_script_language = (m_script_language == eScriptLanguagePython ||
315                                  m_script_language == eScriptLanguageDefault);
316         break;
317 
318       case 'e': {
319         bool success = false;
320         m_stop_on_error = Args::StringToBoolean(option_arg, false, &success);
321         if (!success)
322           error.SetErrorStringWithFormat(
323               "invalid value for stop-on-error: \"%s\"", option_arg);
324       } break;
325 
326       case 'F':
327         m_use_one_liner = false;
328         m_use_script_language = true;
329         m_function_name.assign(option_arg);
330         break;
331 
332       default:
333         break;
334       }
335       return error;
336     }
337 
338     void OptionParsingStarting(ExecutionContext *execution_context) override {
339       m_use_commands = true;
340       m_use_script_language = false;
341       m_script_language = eScriptLanguageNone;
342 
343       m_use_one_liner = false;
344       m_stop_on_error = true;
345       m_one_liner.clear();
346       m_function_name.clear();
347     }
348 
349     const OptionDefinition *GetDefinitions() override { return g_option_table; }
350 
351     // Options table: Required for subclasses of Options.
352 
353     static OptionDefinition g_option_table[];
354 
355     // Instance variables to hold the values for command options.
356 
357     bool m_use_commands;
358     bool m_use_script_language;
359     lldb::ScriptLanguage m_script_language;
360 
361     // Instance variables to hold the values for one_liner options.
362     bool m_use_one_liner;
363     std::string m_one_liner;
364     bool m_stop_on_error;
365     std::string m_function_name;
366   };
367 
368 protected:
369   bool DoExecute(Args &command, CommandReturnObject &result) override {
370     Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
371 
372     if (target == nullptr) {
373       result.AppendError("There is not a current executable; there are no "
374                          "watchpoints to which to add commands");
375       result.SetStatus(eReturnStatusFailed);
376       return false;
377     }
378 
379     const WatchpointList &watchpoints = target->GetWatchpointList();
380     size_t num_watchpoints = watchpoints.GetSize();
381 
382     if (num_watchpoints == 0) {
383       result.AppendError("No watchpoints exist to have commands added");
384       result.SetStatus(eReturnStatusFailed);
385       return false;
386     }
387 
388     if (!m_options.m_use_script_language &&
389         !m_options.m_function_name.empty()) {
390       result.AppendError("need to enable scripting to have a function run as a "
391                          "watchpoint command");
392       result.SetStatus(eReturnStatusFailed);
393       return false;
394     }
395 
396     std::vector<uint32_t> valid_wp_ids;
397     if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, command,
398                                                                valid_wp_ids)) {
399       result.AppendError("Invalid watchpoints specification.");
400       result.SetStatus(eReturnStatusFailed);
401       return false;
402     }
403 
404     result.SetStatus(eReturnStatusSuccessFinishNoResult);
405     const size_t count = valid_wp_ids.size();
406     for (size_t i = 0; i < count; ++i) {
407       uint32_t cur_wp_id = valid_wp_ids.at(i);
408       if (cur_wp_id != LLDB_INVALID_WATCH_ID) {
409         Watchpoint *wp = target->GetWatchpointList().FindByID(cur_wp_id).get();
410         // Sanity check wp first.
411         if (wp == nullptr)
412           continue;
413 
414         WatchpointOptions *wp_options = wp->GetOptions();
415         // Skip this watchpoint if wp_options is not good.
416         if (wp_options == nullptr)
417           continue;
418 
419         // If we are using script language, get the script interpreter
420         // in order to set or collect command callback.  Otherwise, call
421         // the methods associated with this object.
422         if (m_options.m_use_script_language) {
423           // Special handling for one-liner specified inline.
424           if (m_options.m_use_one_liner) {
425             m_interpreter.GetScriptInterpreter()->SetWatchpointCommandCallback(
426                 wp_options, m_options.m_one_liner.c_str());
427           }
428           // Special handling for using a Python function by name
429           // instead of extending the watchpoint callback data structures, we
430           // just automatize
431           // what the user would do manually: make their watchpoint command be a
432           // function call
433           else if (!m_options.m_function_name.empty()) {
434             std::string oneliner(m_options.m_function_name);
435             oneliner += "(frame, wp, internal_dict)";
436             m_interpreter.GetScriptInterpreter()->SetWatchpointCommandCallback(
437                 wp_options, oneliner.c_str());
438           } else {
439             m_interpreter.GetScriptInterpreter()
440                 ->CollectDataForWatchpointCommandCallback(wp_options, result);
441           }
442         } else {
443           // Special handling for one-liner specified inline.
444           if (m_options.m_use_one_liner)
445             SetWatchpointCommandCallback(wp_options,
446                                          m_options.m_one_liner.c_str());
447           else
448             CollectDataForWatchpointCommandCallback(wp_options, result);
449         }
450       }
451     }
452 
453     return result.Succeeded();
454   }
455 
456 private:
457   CommandOptions m_options;
458 };
459 
460 // FIXME: "script-type" needs to have its contents determined dynamically, so
461 // somebody can add a new scripting
462 // language to lldb and have it pickable here without having to change this
463 // enumeration by hand and rebuild lldb proper.
464 
465 static OptionEnumValueElement g_script_option_enumeration[4] = {
466     {eScriptLanguageNone, "command",
467      "Commands are in the lldb command interpreter language"},
468     {eScriptLanguagePython, "python", "Commands are in the Python language."},
469     {eSortOrderByName, "default-script",
470      "Commands are in the default scripting language."},
471     {0, nullptr, nullptr}};
472 
473 OptionDefinition
474     CommandObjectWatchpointCommandAdd::CommandOptions::g_option_table[] = {
475         // clang-format off
476   {LLDB_OPT_SET_1,   false, "one-liner",       'o', OptionParser::eRequiredArgument, nullptr, nullptr,                     0, eArgTypeOneLiner,       "Specify a one-line watchpoint command inline. Be sure to surround it with quotes."},
477   {LLDB_OPT_SET_ALL, false, "stop-on-error",   'e', OptionParser::eRequiredArgument, nullptr, nullptr,                     0, eArgTypeBoolean,        "Specify whether watchpoint command execution should terminate on error."},
478   {LLDB_OPT_SET_ALL, false, "script-type",     's', OptionParser::eRequiredArgument, nullptr, g_script_option_enumeration, 0, eArgTypeNone,           "Specify the language for the commands - if none is specified, the lldb command interpreter will be used."},
479   {LLDB_OPT_SET_2,   false, "python-function", 'F', OptionParser::eRequiredArgument, nullptr, nullptr,                     0, eArgTypePythonFunction, "Give the name of a Python function to run as command for this watchpoint. Be sure to give a module name if appropriate."},
480   {0, false, nullptr, 0, 0, nullptr, nullptr, 0, eArgTypeNone, nullptr }
481         // clang-format on
482 };
483 
484 //-------------------------------------------------------------------------
485 // CommandObjectWatchpointCommandDelete
486 //-------------------------------------------------------------------------
487 
488 class CommandObjectWatchpointCommandDelete : public CommandObjectParsed {
489 public:
490   CommandObjectWatchpointCommandDelete(CommandInterpreter &interpreter)
491       : CommandObjectParsed(interpreter, "delete",
492                             "Delete the set of commands from a watchpoint.",
493                             nullptr) {
494     CommandArgumentEntry arg;
495     CommandArgumentData wp_id_arg;
496 
497     // Define the first (and only) variant of this arg.
498     wp_id_arg.arg_type = eArgTypeWatchpointID;
499     wp_id_arg.arg_repetition = eArgRepeatPlain;
500 
501     // There is only one variant this argument could be; put it into the
502     // argument entry.
503     arg.push_back(wp_id_arg);
504 
505     // Push the data for the first argument into the m_arguments vector.
506     m_arguments.push_back(arg);
507   }
508 
509   ~CommandObjectWatchpointCommandDelete() override = default;
510 
511 protected:
512   bool DoExecute(Args &command, CommandReturnObject &result) override {
513     Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
514 
515     if (target == nullptr) {
516       result.AppendError("There is not a current executable; there are no "
517                          "watchpoints from which to delete commands");
518       result.SetStatus(eReturnStatusFailed);
519       return false;
520     }
521 
522     const WatchpointList &watchpoints = target->GetWatchpointList();
523     size_t num_watchpoints = watchpoints.GetSize();
524 
525     if (num_watchpoints == 0) {
526       result.AppendError("No watchpoints exist to have commands deleted");
527       result.SetStatus(eReturnStatusFailed);
528       return false;
529     }
530 
531     if (command.GetArgumentCount() == 0) {
532       result.AppendError(
533           "No watchpoint specified from which to delete the commands");
534       result.SetStatus(eReturnStatusFailed);
535       return false;
536     }
537 
538     std::vector<uint32_t> valid_wp_ids;
539     if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, command,
540                                                                valid_wp_ids)) {
541       result.AppendError("Invalid watchpoints specification.");
542       result.SetStatus(eReturnStatusFailed);
543       return false;
544     }
545 
546     result.SetStatus(eReturnStatusSuccessFinishNoResult);
547     const size_t count = valid_wp_ids.size();
548     for (size_t i = 0; i < count; ++i) {
549       uint32_t cur_wp_id = valid_wp_ids.at(i);
550       if (cur_wp_id != LLDB_INVALID_WATCH_ID) {
551         Watchpoint *wp = target->GetWatchpointList().FindByID(cur_wp_id).get();
552         if (wp)
553           wp->ClearCallback();
554       } else {
555         result.AppendErrorWithFormat("Invalid watchpoint ID: %u.\n", cur_wp_id);
556         result.SetStatus(eReturnStatusFailed);
557         return false;
558       }
559     }
560     return result.Succeeded();
561   }
562 };
563 
564 //-------------------------------------------------------------------------
565 // CommandObjectWatchpointCommandList
566 //-------------------------------------------------------------------------
567 
568 class CommandObjectWatchpointCommandList : public CommandObjectParsed {
569 public:
570   CommandObjectWatchpointCommandList(CommandInterpreter &interpreter)
571       : CommandObjectParsed(interpreter, "list", "List the script or set of "
572                                                  "commands to be executed when "
573                                                  "the watchpoint is hit.",
574                             nullptr) {
575     CommandArgumentEntry arg;
576     CommandArgumentData wp_id_arg;
577 
578     // Define the first (and only) variant of this arg.
579     wp_id_arg.arg_type = eArgTypeWatchpointID;
580     wp_id_arg.arg_repetition = eArgRepeatPlain;
581 
582     // There is only one variant this argument could be; put it into the
583     // argument entry.
584     arg.push_back(wp_id_arg);
585 
586     // Push the data for the first argument into the m_arguments vector.
587     m_arguments.push_back(arg);
588   }
589 
590   ~CommandObjectWatchpointCommandList() override = default;
591 
592 protected:
593   bool DoExecute(Args &command, CommandReturnObject &result) override {
594     Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
595 
596     if (target == nullptr) {
597       result.AppendError("There is not a current executable; there are no "
598                          "watchpoints for which to list commands");
599       result.SetStatus(eReturnStatusFailed);
600       return false;
601     }
602 
603     const WatchpointList &watchpoints = target->GetWatchpointList();
604     size_t num_watchpoints = watchpoints.GetSize();
605 
606     if (num_watchpoints == 0) {
607       result.AppendError("No watchpoints exist for which to list commands");
608       result.SetStatus(eReturnStatusFailed);
609       return false;
610     }
611 
612     if (command.GetArgumentCount() == 0) {
613       result.AppendError(
614           "No watchpoint specified for which to list the commands");
615       result.SetStatus(eReturnStatusFailed);
616       return false;
617     }
618 
619     std::vector<uint32_t> valid_wp_ids;
620     if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, command,
621                                                                valid_wp_ids)) {
622       result.AppendError("Invalid watchpoints specification.");
623       result.SetStatus(eReturnStatusFailed);
624       return false;
625     }
626 
627     result.SetStatus(eReturnStatusSuccessFinishNoResult);
628     const size_t count = valid_wp_ids.size();
629     for (size_t i = 0; i < count; ++i) {
630       uint32_t cur_wp_id = valid_wp_ids.at(i);
631       if (cur_wp_id != LLDB_INVALID_WATCH_ID) {
632         Watchpoint *wp = target->GetWatchpointList().FindByID(cur_wp_id).get();
633 
634         if (wp) {
635           const WatchpointOptions *wp_options = wp->GetOptions();
636           if (wp_options) {
637             // Get the callback baton associated with the current watchpoint.
638             const Baton *baton = wp_options->GetBaton();
639             if (baton) {
640               result.GetOutputStream().Printf("Watchpoint %u:\n", cur_wp_id);
641               result.GetOutputStream().IndentMore();
642               baton->GetDescription(&result.GetOutputStream(),
643                                     eDescriptionLevelFull);
644               result.GetOutputStream().IndentLess();
645             } else {
646               result.AppendMessageWithFormat(
647                   "Watchpoint %u does not have an associated command.\n",
648                   cur_wp_id);
649             }
650           }
651           result.SetStatus(eReturnStatusSuccessFinishResult);
652         } else {
653           result.AppendErrorWithFormat("Invalid watchpoint ID: %u.\n",
654                                        cur_wp_id);
655           result.SetStatus(eReturnStatusFailed);
656         }
657       }
658     }
659 
660     return result.Succeeded();
661   }
662 };
663 
664 //-------------------------------------------------------------------------
665 // CommandObjectWatchpointCommand
666 //-------------------------------------------------------------------------
667 
668 CommandObjectWatchpointCommand::CommandObjectWatchpointCommand(
669     CommandInterpreter &interpreter)
670     : CommandObjectMultiword(
671           interpreter, "command",
672           "Commands for adding, removing and examining LLDB commands "
673           "executed when the watchpoint is hit (watchpoint 'commmands').",
674           "command <sub-command> [<sub-command-options>] <watchpoint-id>") {
675   CommandObjectSP add_command_object(
676       new CommandObjectWatchpointCommandAdd(interpreter));
677   CommandObjectSP delete_command_object(
678       new CommandObjectWatchpointCommandDelete(interpreter));
679   CommandObjectSP list_command_object(
680       new CommandObjectWatchpointCommandList(interpreter));
681 
682   add_command_object->SetCommandName("watchpoint command add");
683   delete_command_object->SetCommandName("watchpoint command delete");
684   list_command_object->SetCommandName("watchpoint command list");
685 
686   LoadSubCommand("add", add_command_object);
687   LoadSubCommand("delete", delete_command_object);
688   LoadSubCommand("list", list_command_object);
689 }
690 
691 CommandObjectWatchpointCommand::~CommandObjectWatchpointCommand() = default;
692