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