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