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