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     virtual
173     ~CommandObjectWatchpointCommandAdd () {}
174 
175     virtual Options *
176     GetOptions ()
177     {
178         return &m_options;
179     }
180 
181     virtual void
182     IOHandlerActivated (IOHandler &io_handler)
183     {
184         StreamFileSP output_sp(io_handler.GetOutputStreamFile());
185         if (output_sp)
186         {
187             output_sp->PutCString("Enter your debugger command(s).  Type 'DONE' to end.\n");
188             output_sp->Flush();
189         }
190     }
191 
192 
193     virtual void
194     IOHandlerInputComplete (IOHandler &io_handler, std::string &line)
195     {
196         io_handler.SetIsDone(true);
197 
198         // The WatchpointOptions object is owned by the watchpoint or watchpoint location
199         WatchpointOptions *wp_options = (WatchpointOptions *) io_handler.GetUserData();
200         if (wp_options)
201         {
202             std::unique_ptr<WatchpointOptions::CommandData> data_ap(new WatchpointOptions::CommandData());
203             if (data_ap.get())
204             {
205                 data_ap->user_source.SplitIntoLines(line);
206                 BatonSP baton_sp (new WatchpointOptions::CommandBaton (data_ap.release()));
207                 wp_options->SetCallback (WatchpointOptionsCallbackFunction, baton_sp);
208             }
209         }
210     }
211 
212     void
213     CollectDataForWatchpointCommandCallback (WatchpointOptions *wp_options,
214                                              CommandReturnObject &result)
215     {
216         m_interpreter.GetLLDBCommandsFromIOHandler ("> ",           // Prompt
217                                                     *this,          // IOHandlerDelegate
218                                                     true,           // Run IOHandler in async mode
219                                                     wp_options);    // Baton for the "io_handler" that will be passed back into our IOHandlerDelegate functions
220     }
221 
222     /// Set a one-liner as the callback for the watchpoint.
223     void
224     SetWatchpointCommandCallback (WatchpointOptions *wp_options,
225                                   const char *oneliner)
226     {
227         std::unique_ptr<WatchpointOptions::CommandData> data_ap(new WatchpointOptions::CommandData());
228 
229         // It's necessary to set both user_source and script_source to the oneliner.
230         // The former is used to generate callback description (as in watchpoint command list)
231         // while the latter is used for Python to interpret during the actual callback.
232         data_ap->user_source.AppendString (oneliner);
233         data_ap->script_source.assign (oneliner);
234         data_ap->stop_on_error = m_options.m_stop_on_error;
235 
236         BatonSP baton_sp (new WatchpointOptions::CommandBaton (data_ap.release()));
237         wp_options->SetCallback (WatchpointOptionsCallbackFunction, baton_sp);
238 
239         return;
240     }
241 
242     static bool
243     WatchpointOptionsCallbackFunction (void *baton,
244                                        StoppointCallbackContext *context,
245                                        lldb::user_id_t watch_id)
246     {
247         bool ret_value = true;
248         if (baton == NULL)
249             return true;
250 
251 
252         WatchpointOptions::CommandData *data = (WatchpointOptions::CommandData *) baton;
253         StringList &commands = data->user_source;
254 
255         if (commands.GetSize() > 0)
256         {
257             ExecutionContext exe_ctx (context->exe_ctx_ref);
258             Target *target = exe_ctx.GetTargetPtr();
259             if (target)
260             {
261                 CommandReturnObject result;
262                 Debugger &debugger = target->GetDebugger();
263                 // Rig up the results secondary output stream to the debugger's, so the output will come out synchronously
264                 // if the debugger is set up that way.
265 
266                 StreamSP output_stream (debugger.GetAsyncOutputStream());
267                 StreamSP error_stream (debugger.GetAsyncErrorStream());
268                 result.SetImmediateOutputStream (output_stream);
269                 result.SetImmediateErrorStream (error_stream);
270 
271                 CommandInterpreterRunOptions options;
272                 options.SetStopOnContinue (true);
273                 options.SetStopOnError (data->stop_on_error);
274                 options.SetEchoCommands (false);
275                 options.SetPrintResults (true);
276                 options.SetAddToHistory (false);
277 
278                 debugger.GetCommandInterpreter().HandleCommands (commands,
279                                                                  &exe_ctx,
280                                                                  options,
281                                                                  result);
282                 result.GetImmediateOutputStream()->Flush();
283                 result.GetImmediateErrorStream()->Flush();
284            }
285         }
286         return ret_value;
287     }
288 
289     class CommandOptions : public Options
290     {
291     public:
292 
293         CommandOptions (CommandInterpreter &interpreter) :
294             Options (interpreter),
295             m_use_commands (false),
296             m_use_script_language (false),
297             m_script_language (eScriptLanguageNone),
298             m_use_one_liner (false),
299             m_one_liner(),
300             m_function_name()
301         {
302         }
303 
304         virtual
305         ~CommandOptions () {}
306 
307         virtual Error
308         SetOptionValue (uint32_t option_idx, const char *option_arg)
309         {
310             Error error;
311             const int short_option = m_getopt_table[option_idx].val;
312 
313             switch (short_option)
314             {
315             case 'o':
316                 m_use_one_liner = true;
317                 m_one_liner = option_arg;
318                 break;
319 
320             case 's':
321                 m_script_language = (lldb::ScriptLanguage) Args::StringToOptionEnum (option_arg,
322                                                                                      g_option_table[option_idx].enum_values,
323                                                                                      eScriptLanguageNone,
324                                                                                      error);
325 
326                 if (m_script_language == eScriptLanguagePython || m_script_language == eScriptLanguageDefault)
327                 {
328                     m_use_script_language = true;
329                 }
330                 else
331                 {
332                     m_use_script_language = false;
333                 }
334                 break;
335 
336             case 'e':
337                 {
338                     bool success = false;
339                     m_stop_on_error = Args::StringToBoolean(option_arg, false, &success);
340                     if (!success)
341                         error.SetErrorStringWithFormat("invalid value for stop-on-error: \"%s\"", option_arg);
342                 }
343                 break;
344 
345             case 'F':
346                 {
347                     m_use_one_liner = false;
348                     m_use_script_language = true;
349                     m_function_name.assign(option_arg);
350                 }
351                 break;
352 
353             default:
354                 break;
355             }
356             return error;
357         }
358         void
359         OptionParsingStarting ()
360         {
361             m_use_commands = true;
362             m_use_script_language = false;
363             m_script_language = eScriptLanguageNone;
364 
365             m_use_one_liner = false;
366             m_stop_on_error = true;
367             m_one_liner.clear();
368             m_function_name.clear();
369         }
370 
371         const OptionDefinition*
372         GetDefinitions ()
373         {
374             return g_option_table;
375         }
376 
377         // Options table: Required for subclasses of Options.
378 
379         static OptionDefinition g_option_table[];
380 
381         // Instance variables to hold the values for command options.
382 
383         bool m_use_commands;
384         bool m_use_script_language;
385         lldb::ScriptLanguage m_script_language;
386 
387         // Instance variables to hold the values for one_liner options.
388         bool m_use_one_liner;
389         std::string m_one_liner;
390         bool m_stop_on_error;
391         std::string m_function_name;
392     };
393 
394 protected:
395     virtual bool
396     DoExecute (Args& command, CommandReturnObject &result)
397     {
398         Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
399 
400         if (target == NULL)
401         {
402             result.AppendError ("There is not a current executable; there are no watchpoints to which to add commands");
403             result.SetStatus (eReturnStatusFailed);
404             return false;
405         }
406 
407         const WatchpointList &watchpoints = target->GetWatchpointList();
408         size_t num_watchpoints = watchpoints.GetSize();
409 
410         if (num_watchpoints == 0)
411         {
412             result.AppendError ("No watchpoints exist to have commands added");
413             result.SetStatus (eReturnStatusFailed);
414             return false;
415         }
416 
417         if (m_options.m_use_script_language == false && m_options.m_function_name.size())
418         {
419             result.AppendError ("need to enable scripting to have a function run as a watchpoint command");
420             result.SetStatus (eReturnStatusFailed);
421             return false;
422         }
423 
424         std::vector<uint32_t> valid_wp_ids;
425         if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, command, valid_wp_ids))
426         {
427             result.AppendError("Invalid watchpoints specification.");
428             result.SetStatus(eReturnStatusFailed);
429             return false;
430         }
431 
432         result.SetStatus(eReturnStatusSuccessFinishNoResult);
433         const size_t count = valid_wp_ids.size();
434         for (size_t i = 0; i < count; ++i)
435         {
436             uint32_t cur_wp_id = valid_wp_ids.at (i);
437             if (cur_wp_id != LLDB_INVALID_WATCH_ID)
438             {
439                 Watchpoint *wp = target->GetWatchpointList().FindByID (cur_wp_id).get();
440                 // Sanity check wp first.
441                 if (wp == NULL) continue;
442 
443                 WatchpointOptions *wp_options = wp->GetOptions();
444                 // Skip this watchpoint if wp_options is not good.
445                 if (wp_options == NULL) continue;
446 
447                 // If we are using script language, get the script interpreter
448                 // in order to set or collect command callback.  Otherwise, call
449                 // the methods associated with this object.
450                 if (m_options.m_use_script_language)
451                 {
452                     // Special handling for one-liner specified inline.
453                     if (m_options.m_use_one_liner)
454                     {
455                         m_interpreter.GetScriptInterpreter()->SetWatchpointCommandCallback (wp_options,
456                                                                                             m_options.m_one_liner.c_str());
457                     }
458                     // Special handling for using a Python function by name
459                     // instead of extending the watchpoint callback data structures, we just automatize
460                     // what the user would do manually: make their watchpoint command be a function call
461                     else if (m_options.m_function_name.size())
462                     {
463                         std::string oneliner(m_options.m_function_name);
464                         oneliner += "(frame, wp, internal_dict)";
465                         m_interpreter.GetScriptInterpreter()->SetWatchpointCommandCallback (wp_options,
466                                                                                             oneliner.c_str());
467                     }
468                     else
469                     {
470                         m_interpreter.GetScriptInterpreter()->CollectDataForWatchpointCommandCallback (wp_options,
471                                                                                                        result);
472                     }
473                 }
474                 else
475                 {
476                     // Special handling for one-liner specified inline.
477                     if (m_options.m_use_one_liner)
478                         SetWatchpointCommandCallback (wp_options,
479                                                       m_options.m_one_liner.c_str());
480                     else
481                         CollectDataForWatchpointCommandCallback (wp_options,
482                                                                  result);
483                 }
484             }
485         }
486 
487         return result.Succeeded();
488     }
489 
490 private:
491     CommandOptions m_options;
492 };
493 
494 
495 // FIXME: "script-type" needs to have its contents determined dynamically, so somebody can add a new scripting
496 // language to lldb and have it pickable here without having to change this enumeration by hand and rebuild lldb proper.
497 
498 static OptionEnumValueElement
499 g_script_option_enumeration[4] =
500 {
501     { eScriptLanguageNone,    "command",         "Commands are in the lldb command interpreter language"},
502     { eScriptLanguagePython,  "python",          "Commands are in the Python language."},
503     { eSortOrderByName,       "default-script",  "Commands are in the default scripting language."},
504     { 0,                      NULL,              NULL }
505 };
506 
507 OptionDefinition
508 CommandObjectWatchpointCommandAdd::CommandOptions::g_option_table[] =
509 {
510     { LLDB_OPT_SET_1,   false, "one-liner",       'o', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeOneLiner,
511         "Specify a one-line watchpoint command inline. Be sure to surround it with quotes." },
512 
513     { LLDB_OPT_SET_ALL, false, "stop-on-error",   'e', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeBoolean,
514         "Specify whether watchpoint command execution should terminate on error." },
515 
516     { LLDB_OPT_SET_ALL, false, "script-type",     's', OptionParser::eRequiredArgument, NULL, g_script_option_enumeration, 0, eArgTypeNone,
517         "Specify the language for the commands - if none is specified, the lldb command interpreter will be used."},
518 
519     { LLDB_OPT_SET_2,   false, "python-function", 'F', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypePythonFunction,
520         "Give the name of a Python function to run as command for this watchpoint. Be sure to give a module name if appropriate."},
521 
522     { 0, false, NULL, 0, 0, NULL, NULL, 0, eArgTypeNone, NULL }
523 };
524 
525 //-------------------------------------------------------------------------
526 // CommandObjectWatchpointCommandDelete
527 //-------------------------------------------------------------------------
528 
529 class CommandObjectWatchpointCommandDelete : public CommandObjectParsed
530 {
531 public:
532     CommandObjectWatchpointCommandDelete (CommandInterpreter &interpreter) :
533         CommandObjectParsed (interpreter,
534                              "delete",
535                              "Delete the set of commands from a watchpoint.",
536                              NULL)
537     {
538         CommandArgumentEntry arg;
539         CommandArgumentData wp_id_arg;
540 
541         // Define the first (and only) variant of this arg.
542         wp_id_arg.arg_type = eArgTypeWatchpointID;
543         wp_id_arg.arg_repetition = eArgRepeatPlain;
544 
545         // There is only one variant this argument could be; put it into the argument entry.
546         arg.push_back (wp_id_arg);
547 
548         // Push the data for the first argument into the m_arguments vector.
549         m_arguments.push_back (arg);
550     }
551 
552 
553     virtual
554     ~CommandObjectWatchpointCommandDelete () {}
555 
556 protected:
557     virtual bool
558     DoExecute (Args& command, CommandReturnObject &result)
559     {
560         Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
561 
562         if (target == NULL)
563         {
564             result.AppendError ("There is not a current executable; there are no watchpoints from which to delete commands");
565             result.SetStatus (eReturnStatusFailed);
566             return false;
567         }
568 
569         const WatchpointList &watchpoints = target->GetWatchpointList();
570         size_t num_watchpoints = watchpoints.GetSize();
571 
572         if (num_watchpoints == 0)
573         {
574             result.AppendError ("No watchpoints exist to have commands deleted");
575             result.SetStatus (eReturnStatusFailed);
576             return false;
577         }
578 
579         if (command.GetArgumentCount() == 0)
580         {
581             result.AppendError ("No watchpoint specified from which to delete the commands");
582             result.SetStatus (eReturnStatusFailed);
583             return false;
584         }
585 
586         std::vector<uint32_t> valid_wp_ids;
587         if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, command, valid_wp_ids))
588         {
589             result.AppendError("Invalid watchpoints specification.");
590             result.SetStatus(eReturnStatusFailed);
591             return false;
592         }
593 
594         result.SetStatus(eReturnStatusSuccessFinishNoResult);
595         const size_t count = valid_wp_ids.size();
596         for (size_t i = 0; i < count; ++i)
597         {
598             uint32_t cur_wp_id = valid_wp_ids.at (i);
599             if (cur_wp_id != LLDB_INVALID_WATCH_ID)
600             {
601                 Watchpoint *wp = target->GetWatchpointList().FindByID (cur_wp_id).get();
602                 if (wp)
603                     wp->ClearCallback();
604             }
605             else
606             {
607                 result.AppendErrorWithFormat("Invalid watchpoint ID: %u.\n",
608                                              cur_wp_id);
609                 result.SetStatus (eReturnStatusFailed);
610                 return false;
611             }
612         }
613         return result.Succeeded();
614     }
615 };
616 
617 //-------------------------------------------------------------------------
618 // CommandObjectWatchpointCommandList
619 //-------------------------------------------------------------------------
620 
621 class CommandObjectWatchpointCommandList : public CommandObjectParsed
622 {
623 public:
624     CommandObjectWatchpointCommandList (CommandInterpreter &interpreter) :
625         CommandObjectParsed (interpreter,
626                              "list",
627                              "List the script or set of commands to be executed when the watchpoint is hit.",
628                               NULL)
629     {
630         CommandArgumentEntry arg;
631         CommandArgumentData wp_id_arg;
632 
633         // Define the first (and only) variant of this arg.
634         wp_id_arg.arg_type = eArgTypeWatchpointID;
635         wp_id_arg.arg_repetition = eArgRepeatPlain;
636 
637         // There is only one variant this argument could be; put it into the argument entry.
638         arg.push_back (wp_id_arg);
639 
640         // Push the data for the first argument into the m_arguments vector.
641         m_arguments.push_back (arg);
642     }
643 
644     virtual
645     ~CommandObjectWatchpointCommandList () {}
646 
647 protected:
648     virtual bool
649     DoExecute (Args& command,
650              CommandReturnObject &result)
651     {
652         Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
653 
654         if (target == NULL)
655         {
656             result.AppendError ("There is not a current executable; there are no watchpoints for which to list commands");
657             result.SetStatus (eReturnStatusFailed);
658             return false;
659         }
660 
661         const WatchpointList &watchpoints = target->GetWatchpointList();
662         size_t num_watchpoints = watchpoints.GetSize();
663 
664         if (num_watchpoints == 0)
665         {
666             result.AppendError ("No watchpoints exist for which to list commands");
667             result.SetStatus (eReturnStatusFailed);
668             return false;
669         }
670 
671         if (command.GetArgumentCount() == 0)
672         {
673             result.AppendError ("No watchpoint specified for which to list the commands");
674             result.SetStatus (eReturnStatusFailed);
675             return false;
676         }
677 
678         std::vector<uint32_t> valid_wp_ids;
679         if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, command, valid_wp_ids))
680         {
681             result.AppendError("Invalid watchpoints specification.");
682             result.SetStatus(eReturnStatusFailed);
683             return false;
684         }
685 
686         result.SetStatus(eReturnStatusSuccessFinishNoResult);
687         const size_t count = valid_wp_ids.size();
688         for (size_t i = 0; i < count; ++i)
689         {
690             uint32_t cur_wp_id = valid_wp_ids.at (i);
691             if (cur_wp_id != LLDB_INVALID_WATCH_ID)
692             {
693                 Watchpoint *wp = target->GetWatchpointList().FindByID (cur_wp_id).get();
694 
695                 if (wp)
696                 {
697                     const WatchpointOptions *wp_options = wp->GetOptions();
698                     if (wp_options)
699                     {
700                         // Get the callback baton associated with the current watchpoint.
701                         const Baton *baton = wp_options->GetBaton();
702                         if (baton)
703                         {
704                             result.GetOutputStream().Printf ("Watchpoint %u:\n", cur_wp_id);
705                             result.GetOutputStream().IndentMore ();
706                             baton->GetDescription(&result.GetOutputStream(), eDescriptionLevelFull);
707                             result.GetOutputStream().IndentLess ();
708                         }
709                         else
710                         {
711                             result.AppendMessageWithFormat ("Watchpoint %u does not have an associated command.\n",
712                                                             cur_wp_id);
713                         }
714                     }
715                     result.SetStatus (eReturnStatusSuccessFinishResult);
716                 }
717                 else
718                 {
719                     result.AppendErrorWithFormat("Invalid watchpoint ID: %u.\n", cur_wp_id);
720                     result.SetStatus (eReturnStatusFailed);
721                 }
722             }
723         }
724 
725         return result.Succeeded();
726     }
727 };
728 
729 //-------------------------------------------------------------------------
730 // CommandObjectWatchpointCommand
731 //-------------------------------------------------------------------------
732 
733 CommandObjectWatchpointCommand::CommandObjectWatchpointCommand (CommandInterpreter &interpreter) :
734     CommandObjectMultiword (interpreter,
735                             "command",
736                             "A set of commands for adding, removing and examining bits of code to be executed when the watchpoint is hit (watchpoint 'commmands').",
737                             "command <sub-command> [<sub-command-options>] <watchpoint-id>")
738 {
739     CommandObjectSP add_command_object (new CommandObjectWatchpointCommandAdd (interpreter));
740     CommandObjectSP delete_command_object (new CommandObjectWatchpointCommandDelete (interpreter));
741     CommandObjectSP list_command_object (new CommandObjectWatchpointCommandList (interpreter));
742 
743     add_command_object->SetCommandName ("watchpoint command add");
744     delete_command_object->SetCommandName ("watchpoint command delete");
745     list_command_object->SetCommandName ("watchpoint command list");
746 
747     LoadSubCommand ("add",    add_command_object);
748     LoadSubCommand ("delete", delete_command_object);
749     LoadSubCommand ("list",   list_command_object);
750 }
751 
752 CommandObjectWatchpointCommand::~CommandObjectWatchpointCommand ()
753 {
754 }
755 
756 
757