1 //===-- CommandObjectBreakpointCommand.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 "CommandObjectBreakpointCommand.h"
15 #include "CommandObjectBreakpoint.h"
16 
17 #include "lldb/Interpreter/CommandInterpreter.h"
18 #include "lldb/Interpreter/CommandReturnObject.h"
19 #include "lldb/Target/Target.h"
20 #include "lldb/Target/Thread.h"
21 #include "lldb/Breakpoint/BreakpointIDList.h"
22 #include "lldb/Breakpoint/Breakpoint.h"
23 #include "lldb/Breakpoint/BreakpointLocation.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 // CommandObjectBreakpointCommandAdd::CommandOptions
32 //-------------------------------------------------------------------------
33 
34 CommandObjectBreakpointCommandAdd::CommandOptions::CommandOptions () :
35     Options (),
36     m_use_commands (false),
37     m_use_script_language (false),
38     m_script_language (eScriptLanguageNone),
39     m_use_one_liner (false),
40     m_one_liner()
41 {
42 }
43 
44 CommandObjectBreakpointCommandAdd::CommandOptions::~CommandOptions ()
45 {
46 }
47 
48 lldb::OptionDefinition
49 CommandObjectBreakpointCommandAdd::CommandOptions::g_option_table[] =
50 {
51     { LLDB_OPT_SET_ALL, false, "one-liner", 'o', required_argument, NULL, 0, "<one-liner>",
52         "Specify a one-line breakpoint command inline. Be sure to surround it with quotes." },
53 
54     { LLDB_OPT_SET_1, true, "script",    's', no_argument, NULL, 0, NULL,
55         "Write the breakpoint command script in the default scripting language."},
56 
57     { LLDB_OPT_SET_2, true, "python",    'p', no_argument, NULL, 0, NULL,
58         "Write the breakpoint command script in the Python scripting language."},
59 
60     { LLDB_OPT_SET_3, true, "commands",  'c', no_argument, NULL, 0, NULL,
61         "Write the breakpoint command script using standard debugger commands."},
62 
63     { 0, false, NULL, 0, 0, NULL, 0, NULL, NULL }
64 };
65 
66 const lldb::OptionDefinition*
67 CommandObjectBreakpointCommandAdd::CommandOptions::GetDefinitions ()
68 {
69     return g_option_table;
70 }
71 
72 
73 Error
74 CommandObjectBreakpointCommandAdd::CommandOptions::SetOptionValue
75 (
76     int option_idx,
77     const char *option_arg
78 )
79 {
80     Error error;
81     char short_option = (char) m_getopt_table[option_idx].val;
82 
83     switch (short_option)
84       {
85       case 'o':
86         m_use_one_liner = true;
87         m_one_liner = option_arg;
88         break;
89       case 's':
90         m_use_commands = false;
91         m_use_script_language = true;
92         m_script_language = eScriptLanguageDefault;
93         break;
94       case 'p':
95         m_use_commands = false;
96         m_use_script_language = true;
97         m_script_language = eScriptLanguagePython;
98         break;
99       case 'c':
100         m_use_commands = true;
101         m_use_script_language = false;
102         m_script_language = eScriptLanguageNone;
103         break;
104       default:
105         break;
106       }
107     return error;
108 }
109 
110 void
111 CommandObjectBreakpointCommandAdd::CommandOptions::ResetOptionValues ()
112 {
113     Options::ResetOptionValues();
114 
115     m_use_commands = false;
116     m_use_script_language = false;
117     m_script_language = eScriptLanguageNone;
118 
119     m_use_one_liner = false;
120     m_one_liner.clear();
121 }
122 
123 //-------------------------------------------------------------------------
124 // CommandObjectBreakpointCommandAdd
125 //-------------------------------------------------------------------------
126 
127 
128 CommandObjectBreakpointCommandAdd::CommandObjectBreakpointCommandAdd (CommandInterpreter &interpreter) :
129     CommandObject (interpreter,
130                    "add",
131                    "Add a set of commands to a breakpoint, to be executed whenever the breakpoint is hit.",
132                    "breakpoint command add <cmd-options> <breakpoint-id>")
133 {
134     SetHelpLong (
135 "\nGeneral information about entering breakpoint commands \n\
136 ------------------------------------------------------ \n\
137  \n\
138 This command will cause you to be prompted to enter the command or set \n\
139 of commands you wish to be executed when the specified breakpoint is \n\
140 hit.  You will be told to enter your command(s), and will see a '> ' \n\
141 prompt. Because you can enter one or many commands to be executed when \n\
142 a breakpoint is hit, you will continue to be prompted after each \n\
143 new-line that you enter, until you enter the word 'DONE', which will \n\
144 cause the commands you have entered to be stored with the breakpoint \n\
145 and executed when the breakpoint is hit. \n\
146  \n\
147 Syntax checking is not necessarily done when breakpoint commands are \n\
148 entered.  An improperly written breakpoint command will attempt to get \n\
149 executed when the breakpoint gets hit, and usually silently fail.  If \n\
150 your breakpoint command does not appear to be getting executed, go \n\
151 back and check your syntax. \n\
152  \n\
153  \n\
154 Special information about PYTHON breakpoint commands \n\
155 ---------------------------------------------------- \n\
156  \n\
157 You may enter either one line of Python or multiple lines of Python \n\
158 (including defining whole functions, if desired).  If you enter a \n\
159 single line of Python, that will be passed to the Python interpreter \n\
160 'as is' when the breakpoint gets hit.  If you enter function \n\
161 definitions, they will be passed to the Python interpreter as soon as \n\
162 you finish entering the breakpoint command, and they can be called \n\
163 later (don't forget to add calls to them, if you want them called when \n\
164 the breakpoint is hit).  If you enter multiple lines of Python that \n\
165 are not function definitions, they will be collected into a new, \n\
166 automatically generated Python function, and a call to the newly \n\
167 generated function will be attached to the breakpoint.  Important \n\
168 Note: Because loose Python code gets collected into functions, if you \n\
169 want to access global variables in the 'loose' code, you need to \n\
170 specify that they are global, using the 'global' keyword.  Be sure to \n\
171 use correct Python syntax, including indentation, when entering Python \n\
172 breakpoint commands. \n\
173  \n\
174 Example Python one-line breakpoint command: \n\
175  \n\
176 (lldb) breakpoint command add -p 1 \n\
177 Enter your Python command(s). Type 'DONE' to end. \n\
178 > print \"Hit this breakpoint!\" \n\
179 > DONE \n\
180  \n\
181 As a convenience, this also works for a short Python one-liner: \n\
182 (lldb) breakpoint command add -p 1 -o \"import time; print time.asctime()\" \n\
183 (lldb) run \n\
184 Launching '.../a.out'  (x86_64) \n\
185 (lldb) Fri Sep 10 12:17:45 2010 \n\
186 Process 21778 Stopped \n\
187 * thread #1: tid = 0x2e03, 0x0000000100000de8 a.out`c + 7 at main.c:39, stop reason = breakpoint 1.1, queue = com.apple.main-thread \n\
188   36   	\n\
189   37   	int c(int val)\n\
190   38   	{\n\
191   39 ->	    return val + 3;\n\
192   40   	}\n\
193   41   	\n\
194   42   	int main (int argc, char const *argv[])\n\
195 (lldb) \n\
196  \n\
197 Example multiple line Python breakpoint command, using function definition: \n\
198  \n\
199 (lldb) breakpoint command add -p 1 \n\
200 Enter your Python command(s). Type 'DONE' to end. \n\
201 > def breakpoint_output (bp_no): \n\
202 >     out_string = \"Hit breakpoint number \" + repr (bp_no) \n\
203 >     print out_string \n\
204 >     return True \n\
205 > breakpoint_output (1) \n\
206 > DONE \n\
207  \n\
208  \n\
209 Example multiple line Python breakpoint command, using 'loose' Python: \n\
210  \n\
211 (lldb) breakpoint command add -p 1 \n\
212 Enter your Python command(s). Type 'DONE' to end. \n\
213 > global bp_count \n\
214 > bp_count = bp_count + 1 \n\
215 > print \"Hit this breakpoint \" + repr(bp_count) + \" times!\" \n\
216 > DONE \n\
217  \n\
218 In this case, since there is a reference to a global variable, \n\
219 'bp_count', you will also need to make sure 'bp_count' exists and is \n\
220 initialized: \n\
221  \n\
222 (lldb) script \n\
223 >>> bp_count = 0 \n\
224 >>> quit() \n\
225  \n\
226 (lldb)  \n\
227  \n\
228  \n\
229 Final Note:  If you get a warning that no breakpoint command was generated, \n\
230 but you did not get any syntax errors, you probably forgot to add a call \n\
231 to your functions. \n\
232  \n\
233 Special information about debugger command breakpoint commands \n\
234 -------------------------------------------------------------- \n\
235  \n\
236 You may enter any debugger command, exactly as you would at the \n\
237 debugger prompt.  You may enter as many debugger commands as you like, \n\
238 but do NOT enter more than one command per line. \n" );
239 }
240 
241 CommandObjectBreakpointCommandAdd::~CommandObjectBreakpointCommandAdd ()
242 {
243 }
244 
245 bool
246 CommandObjectBreakpointCommandAdd::Execute
247 (
248     Args& command,
249     CommandReturnObject &result
250 )
251 {
252     Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
253 
254     if (target == NULL)
255     {
256         result.AppendError ("There is not a current executable; there are no breakpoints to which to add commands");
257         result.SetStatus (eReturnStatusFailed);
258         return false;
259     }
260 
261     const BreakpointList &breakpoints = target->GetBreakpointList();
262     size_t num_breakpoints = breakpoints.GetSize();
263 
264     if (num_breakpoints == 0)
265     {
266         result.AppendError ("No breakpoints exist to have commands added");
267         result.SetStatus (eReturnStatusFailed);
268         return false;
269     }
270 
271     if (command.GetArgumentCount() == 0)
272     {
273         result.AppendError ("No breakpoint specified to which to add the commands");
274         result.SetStatus (eReturnStatusFailed);
275         return false;
276     }
277 
278     BreakpointIDList valid_bp_ids;
279     CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs (command, target, result, &valid_bp_ids);
280 
281     if (result.Succeeded())
282     {
283         const size_t count = valid_bp_ids.GetSize();
284         for (size_t i = 0; i < count; ++i)
285         {
286             BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex (i);
287             if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID)
288             {
289                 Breakpoint *bp = target->GetBreakpointByID (cur_bp_id.GetBreakpointID()).get();
290                 BreakpointOptions *bp_options = NULL;
291                 if (cur_bp_id.GetLocationID() == LLDB_INVALID_BREAK_ID)
292                 {
293                     // This breakpoint does not have an associated location.
294                     bp_options = bp->GetOptions();
295                 }
296                 else
297                 {
298                     BreakpointLocationSP bp_loc_sp(bp->FindLocationByID (cur_bp_id.GetLocationID()));
299                     // This breakpoint does have an associated location.
300                     // Get its breakpoint options.
301                     if (bp_loc_sp)
302                         bp_options = bp_loc_sp->GetLocationOptions();
303                 }
304 
305                 // Skip this breakpoiont if bp_options is not good.
306                 if (bp_options == NULL) continue;
307 
308                 // If we are using script language, get the script interpreter
309                 // in order to set or collect command callback.  Otherwise, call
310                 // the methods associated with this object.
311                 if (m_options.m_use_script_language)
312                 {
313                     // Special handling for one-liner specified inline.
314                     if (m_options.m_use_one_liner)
315                         m_interpreter.GetScriptInterpreter()->SetBreakpointCommandCallback (bp_options,
316                                                                                             m_options.m_one_liner.c_str());
317                     else
318                         m_interpreter.GetScriptInterpreter()->CollectDataForBreakpointCommandCallback (bp_options,
319                                                                                                        result);
320                 }
321                 else
322                 {
323                     // Special handling for one-liner specified inline.
324                     if (m_options.m_use_one_liner)
325                         SetBreakpointCommandCallback (bp_options,
326                                                       m_options.m_one_liner.c_str());
327                     else
328                         CollectDataForBreakpointCommandCallback (bp_options,
329                                                                  result);
330                 }
331             }
332         }
333     }
334 
335     return result.Succeeded();
336 }
337 
338 Options *
339 CommandObjectBreakpointCommandAdd::GetOptions ()
340 {
341     return &m_options;
342 }
343 
344 const char *g_reader_instructions = "Enter your debugger command(s).  Type 'DONE' to end.";
345 
346 void
347 CommandObjectBreakpointCommandAdd::CollectDataForBreakpointCommandCallback
348 (
349     BreakpointOptions *bp_options,
350     CommandReturnObject &result
351 )
352 {
353     InputReaderSP reader_sp (new InputReader(m_interpreter.GetDebugger()));
354     std::auto_ptr<BreakpointOptions::CommandData> data_ap(new BreakpointOptions::CommandData());
355     if (reader_sp && data_ap.get())
356     {
357         BatonSP baton_sp (new BreakpointOptions::CommandBaton (data_ap.release()));
358         bp_options->SetCallback (CommandObjectBreakpointCommand::BreakpointOptionsCallbackFunction, baton_sp);
359 
360         Error err (reader_sp->Initialize (CommandObjectBreakpointCommandAdd::GenerateBreakpointCommandCallback,
361                                           bp_options,                   // baton
362                                           eInputReaderGranularityLine,  // token size, to pass to callback function
363                                           "DONE",                       // end token
364                                           "> ",                         // prompt
365                                           true));                       // echo input
366         if (err.Success())
367         {
368             m_interpreter.GetDebugger().PushInputReader (reader_sp);
369             result.SetStatus (eReturnStatusSuccessFinishNoResult);
370         }
371         else
372         {
373             result.AppendError (err.AsCString());
374             result.SetStatus (eReturnStatusFailed);
375         }
376     }
377     else
378     {
379         result.AppendError("out of memory");
380         result.SetStatus (eReturnStatusFailed);
381     }
382 
383 }
384 
385 // Set a one-liner as the callback for the breakpoint.
386 void
387 CommandObjectBreakpointCommandAdd::SetBreakpointCommandCallback (BreakpointOptions *bp_options,
388                                                                  const char *oneliner)
389 {
390     std::auto_ptr<BreakpointOptions::CommandData> data_ap(new BreakpointOptions::CommandData());
391 
392     // It's necessary to set both user_source and script_source to the oneliner.
393     // The former is used to generate callback description (as in breakpoint command list)
394     // while the latter is used for Python to interpret during the actual callback.
395     data_ap->user_source.AppendString (oneliner);
396     data_ap->script_source.AppendString (oneliner);
397 
398     BatonSP baton_sp (new BreakpointOptions::CommandBaton (data_ap.release()));
399     bp_options->SetCallback (CommandObjectBreakpointCommand::BreakpointOptionsCallbackFunction, baton_sp);
400 
401     return;
402 }
403 
404 size_t
405 CommandObjectBreakpointCommandAdd::GenerateBreakpointCommandCallback
406 (
407     void *baton,
408     InputReader &reader,
409     lldb::InputReaderAction notification,
410     const char *bytes,
411     size_t bytes_len
412 )
413 {
414     FILE *out_fh = reader.GetDebugger().GetOutputFileHandle();
415 
416     switch (notification)
417     {
418     case eInputReaderActivate:
419         if (out_fh)
420         {
421             ::fprintf (out_fh, "%s\n", g_reader_instructions);
422             if (reader.GetPrompt())
423                 ::fprintf (out_fh, "%s", reader.GetPrompt());
424         }
425         break;
426 
427     case eInputReaderDeactivate:
428         break;
429 
430     case eInputReaderReactivate:
431         if (out_fh && reader.GetPrompt())
432             ::fprintf (out_fh, "%s", reader.GetPrompt());
433         break;
434 
435     case eInputReaderGotToken:
436         if (bytes && bytes_len && baton)
437         {
438             BreakpointOptions *bp_options = (BreakpointOptions *) baton;
439             if (bp_options)
440             {
441                 Baton *bp_options_baton = bp_options->GetBaton();
442                 if (bp_options_baton)
443                     ((BreakpointOptions::CommandData *)bp_options_baton->m_data)->user_source.AppendString (bytes, bytes_len);
444             }
445         }
446         if (out_fh && !reader.IsDone() && reader.GetPrompt())
447             ::fprintf (out_fh, "%s", reader.GetPrompt());
448         break;
449 
450     case eInputReaderDone:
451         break;
452     }
453 
454     return bytes_len;
455 }
456 
457 
458 //-------------------------------------------------------------------------
459 // CommandObjectBreakpointCommandRemove
460 //-------------------------------------------------------------------------
461 
462 CommandObjectBreakpointCommandRemove::CommandObjectBreakpointCommandRemove (CommandInterpreter &interpreter) :
463     CommandObject (interpreter,
464                    "remove",
465                    "Remove the set of commands from a breakpoint.",
466                    "breakpoint command remove <breakpoint-id>")
467 {
468 }
469 
470 CommandObjectBreakpointCommandRemove::~CommandObjectBreakpointCommandRemove ()
471 {
472 }
473 
474 bool
475 CommandObjectBreakpointCommandRemove::Execute
476 (
477     Args& command,
478     CommandReturnObject &result
479 )
480 {
481     Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
482 
483     if (target == NULL)
484     {
485         result.AppendError ("There is not a current executable; there are no breakpoints from which to remove commands");
486         result.SetStatus (eReturnStatusFailed);
487         return false;
488     }
489 
490     const BreakpointList &breakpoints = target->GetBreakpointList();
491     size_t num_breakpoints = breakpoints.GetSize();
492 
493     if (num_breakpoints == 0)
494     {
495         result.AppendError ("No breakpoints exist to have commands removed");
496         result.SetStatus (eReturnStatusFailed);
497         return false;
498     }
499 
500     if (command.GetArgumentCount() == 0)
501     {
502         result.AppendError ("No breakpoint specified from which to remove the commands");
503         result.SetStatus (eReturnStatusFailed);
504         return false;
505     }
506 
507     BreakpointIDList valid_bp_ids;
508     CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs (command, target, result, &valid_bp_ids);
509 
510     if (result.Succeeded())
511     {
512         const size_t count = valid_bp_ids.GetSize();
513         for (size_t i = 0; i < count; ++i)
514         {
515             BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex (i);
516             if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID)
517             {
518                 Breakpoint *bp = target->GetBreakpointByID (cur_bp_id.GetBreakpointID()).get();
519                 if (cur_bp_id.GetLocationID() != LLDB_INVALID_BREAK_ID)
520                 {
521                     BreakpointLocationSP bp_loc_sp (bp->FindLocationByID (cur_bp_id.GetLocationID()));
522                     if (bp_loc_sp)
523                         bp_loc_sp->ClearCallback();
524                     else
525                     {
526                         result.AppendErrorWithFormat("Invalid breakpoint ID: %u.%u.\n",
527                                                      cur_bp_id.GetBreakpointID(),
528                                                      cur_bp_id.GetLocationID());
529                         result.SetStatus (eReturnStatusFailed);
530                         return false;
531                     }
532                 }
533                 else
534                 {
535                     bp->ClearCallback();
536                 }
537             }
538         }
539     }
540     return result.Succeeded();
541 }
542 
543 
544 //-------------------------------------------------------------------------
545 // CommandObjectBreakpointCommandList
546 //-------------------------------------------------------------------------
547 
548 CommandObjectBreakpointCommandList::CommandObjectBreakpointCommandList (CommandInterpreter &interpreter) :
549     CommandObject (interpreter,
550                    "list",
551                    "List the script or set of commands to be executed when the breakpoint is hit.",
552                    "breakpoint command list <breakpoint-id>")
553 {
554 }
555 
556 CommandObjectBreakpointCommandList::~CommandObjectBreakpointCommandList ()
557 {
558 }
559 
560 bool
561 CommandObjectBreakpointCommandList::Execute
562 (
563     Args& command,
564     CommandReturnObject &result
565 )
566 {
567     Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
568 
569     if (target == NULL)
570     {
571         result.AppendError ("There is not a current executable; there are no breakpoints for which to list commands");
572         result.SetStatus (eReturnStatusFailed);
573         return false;
574     }
575 
576     const BreakpointList &breakpoints = target->GetBreakpointList();
577     size_t num_breakpoints = breakpoints.GetSize();
578 
579     if (num_breakpoints == 0)
580     {
581         result.AppendError ("No breakpoints exist for which to list commands");
582         result.SetStatus (eReturnStatusFailed);
583         return false;
584     }
585 
586     if (command.GetArgumentCount() == 0)
587     {
588         result.AppendError ("No breakpoint specified for which to list the commands");
589         result.SetStatus (eReturnStatusFailed);
590         return false;
591     }
592 
593     BreakpointIDList valid_bp_ids;
594     CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs (command, target, result, &valid_bp_ids);
595 
596     if (result.Succeeded())
597     {
598         const size_t count = valid_bp_ids.GetSize();
599         for (size_t i = 0; i < count; ++i)
600         {
601             BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex (i);
602             if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID)
603             {
604                 Breakpoint *bp = target->GetBreakpointByID (cur_bp_id.GetBreakpointID()).get();
605 
606                 if (bp)
607                 {
608                     const BreakpointOptions *bp_options = NULL;
609                     if (cur_bp_id.GetLocationID() != LLDB_INVALID_BREAK_ID)
610                     {
611                         BreakpointLocationSP bp_loc_sp(bp->FindLocationByID (cur_bp_id.GetLocationID()));
612                         if (bp_loc_sp)
613                             bp_options = bp_loc_sp->GetOptionsNoCreate();
614                         else
615                         {
616                             result.AppendErrorWithFormat("Invalid breakpoint ID: %u.%u.\n",
617                                                          cur_bp_id.GetBreakpointID(),
618                                                          cur_bp_id.GetLocationID());
619                             result.SetStatus (eReturnStatusFailed);
620                             return false;
621                         }
622                     }
623                     else
624                     {
625                         bp_options = bp->GetOptions();
626                     }
627 
628                     if (bp_options)
629                     {
630                         StreamString id_str;
631                         BreakpointID::GetCanonicalReference (&id_str, cur_bp_id.GetBreakpointID(), cur_bp_id.GetLocationID());
632                         const Baton *baton = bp_options->GetBaton();
633                         if (baton)
634                         {
635                             result.GetOutputStream().Printf ("Breakpoint %s:\n", id_str.GetData());
636                             result.GetOutputStream().IndentMore ();
637                             baton->GetDescription(&result.GetOutputStream(), eDescriptionLevelFull);
638                             result.GetOutputStream().IndentLess ();
639                         }
640                         else
641                         {
642                             result.AppendMessageWithFormat ("Breakpoint %s does not have an associated command.\n", id_str.GetData());
643                         }
644                     }
645                     result.SetStatus (eReturnStatusSuccessFinishResult);
646                 }
647                 else
648                 {
649                     result.AppendErrorWithFormat("Invalid breakpoint ID: %u.\n", cur_bp_id.GetBreakpointID());
650                     result.SetStatus (eReturnStatusFailed);
651                 }
652 
653             }
654         }
655     }
656 
657     return result.Succeeded();
658 }
659 
660 //-------------------------------------------------------------------------
661 // CommandObjectBreakpointCommand
662 //-------------------------------------------------------------------------
663 
664 CommandObjectBreakpointCommand::CommandObjectBreakpointCommand (CommandInterpreter &interpreter) :
665     CommandObjectMultiword (interpreter,
666                             "command",
667                             "A set of commands for adding, removing and examining bits of code to be executed when the breakpoint is hit (breakpoint 'commmands').",
668                             "command <sub-command> [<sub-command-options>] <breakpoint-id>")
669 {
670     bool status;
671     CommandObjectSP add_command_object (new CommandObjectBreakpointCommandAdd (interpreter));
672     CommandObjectSP remove_command_object (new CommandObjectBreakpointCommandRemove (interpreter));
673     CommandObjectSP list_command_object (new CommandObjectBreakpointCommandList (interpreter));
674 
675     add_command_object->SetCommandName ("breakpoint command add");
676     remove_command_object->SetCommandName ("breakpoint command remove");
677     list_command_object->SetCommandName ("breakpoint command list");
678 
679     status = LoadSubCommand ("add",    add_command_object);
680     status = LoadSubCommand ("remove", remove_command_object);
681     status = LoadSubCommand ("list",   list_command_object);
682 }
683 
684 
685 CommandObjectBreakpointCommand::~CommandObjectBreakpointCommand ()
686 {
687 }
688 
689 bool
690 CommandObjectBreakpointCommand::BreakpointOptionsCallbackFunction
691 (
692     void *baton,
693     StoppointCallbackContext *context,
694     lldb::user_id_t break_id,
695     lldb::user_id_t break_loc_id
696 )
697 {
698     bool ret_value = true;
699     if (baton == NULL)
700         return true;
701 
702 
703     BreakpointOptions::CommandData *data = (BreakpointOptions::CommandData *) baton;
704     StringList &commands = data->user_source;
705 
706     if (commands.GetSize() > 0)
707     {
708         uint32_t num_commands = commands.GetSize();
709         CommandReturnObject result;
710         if (context->exe_ctx.target)
711         {
712 
713             Debugger &debugger = context->exe_ctx.target->GetDebugger();
714             CommandInterpreter &interpreter = debugger.GetCommandInterpreter();
715 
716             FILE *out_fh = debugger.GetOutputFileHandle();
717             FILE *err_fh = debugger.GetErrorFileHandle();
718 
719             uint32_t i;
720             for (i = 0; i < num_commands; ++i)
721             {
722 
723                 // First time through we use the context from the stoppoint, after that we use whatever
724                 // has been set by the previous command.
725 
726                 if (!interpreter.HandleCommand (commands.GetStringAtIndex(i), false, result, &context->exe_ctx))
727                     break;
728 
729                 // FIXME: This isn't really the right way to do this.  We should be able to peek at the public
730                 // to see if there is any new events, but that is racey, since the internal process thread has to run and
731                 // deliver the event to the public queue before a run will show up.  So for now we check
732                 // the internal thread state.
733 
734                 lldb::StateType internal_state = context->exe_ctx.process->GetPrivateState();
735                 if (internal_state != eStateStopped)
736                 {
737                     if (i < num_commands - 1)
738                     {
739                         if (out_fh)
740                             ::fprintf (out_fh, "Short-circuiting command execution because target state changed to %s."
741                                                " last command: \"%s\"\n", StateAsCString(internal_state),
742                                                commands.GetStringAtIndex(i));
743                     }
744                     break;
745                 }
746 
747                 if (out_fh)
748                     ::fprintf (out_fh, "%s", result.GetErrorStream().GetData());
749                 if (err_fh)
750                     ::fprintf (err_fh, "%s", result.GetOutputStream().GetData());
751                 result.Clear();
752                 result.SetStatus (eReturnStatusSuccessFinishNoResult);
753             }
754 
755             if (err_fh && !result.Succeeded() && i < num_commands)
756                 ::fprintf (err_fh, "Attempt to execute '%s' failed.\n", commands.GetStringAtIndex(i));
757 
758             if (out_fh)
759                 ::fprintf (out_fh, "%s", result.GetErrorStream().GetData());
760 
761             if (err_fh)
762                 ::fprintf (err_fh, "%s", result.GetOutputStream().GetData());
763         }
764     }
765     return ret_value;
766 }
767 
768