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()
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() :
284             Options(),
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,
298                        ExecutionContext *execution_context) 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(ExecutionContext *execution_context) 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, "command", "Commands for adding, removing and examining LLDB commands "
713                                                      "executed when the watchpoint is hit (watchpoint 'commmands').",
714                              "command <sub-command> [<sub-command-options>] <watchpoint-id>")
715 {
716     CommandObjectSP add_command_object (new CommandObjectWatchpointCommandAdd (interpreter));
717     CommandObjectSP delete_command_object (new CommandObjectWatchpointCommandDelete (interpreter));
718     CommandObjectSP list_command_object (new CommandObjectWatchpointCommandList (interpreter));
719 
720     add_command_object->SetCommandName ("watchpoint command add");
721     delete_command_object->SetCommandName ("watchpoint command delete");
722     list_command_object->SetCommandName ("watchpoint command list");
723 
724     LoadSubCommand ("add",    add_command_object);
725     LoadSubCommand ("delete", delete_command_object);
726     LoadSubCommand ("list",   list_command_object);
727 }
728 
729 CommandObjectWatchpointCommand::~CommandObjectWatchpointCommand() = default;
730