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-liner inline." },
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 Special information about debugger command breakpoint commands \n\
229 -------------------------------------------------------------- \n\
230  \n\
231 You may enter any debugger command, exactly as you would at the \n\
232 debugger prompt.  You may enter as many debugger commands as you like, \n\
233 but do NOT enter more than one command per line. \n" );
234 }
235 
236 CommandObjectBreakpointCommandAdd::~CommandObjectBreakpointCommandAdd ()
237 {
238 }
239 
240 bool
241 CommandObjectBreakpointCommandAdd::Execute
242 (
243     Args& command,
244     CommandReturnObject &result
245 )
246 {
247     Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
248 
249     if (target == NULL)
250     {
251         result.AppendError ("There is not a current executable; there are no breakpoints to which to add commands");
252         result.SetStatus (eReturnStatusFailed);
253         return false;
254     }
255 
256     const BreakpointList &breakpoints = target->GetBreakpointList();
257     size_t num_breakpoints = breakpoints.GetSize();
258 
259     if (num_breakpoints == 0)
260     {
261         result.AppendError ("No breakpoints exist to have commands added");
262         result.SetStatus (eReturnStatusFailed);
263         return false;
264     }
265 
266     if (command.GetArgumentCount() == 0)
267     {
268         result.AppendError ("No breakpoint specified to which to add the commands");
269         result.SetStatus (eReturnStatusFailed);
270         return false;
271     }
272 
273     BreakpointIDList valid_bp_ids;
274     CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs (command, target, result, &valid_bp_ids);
275 
276     if (result.Succeeded())
277     {
278         const size_t count = valid_bp_ids.GetSize();
279         for (size_t i = 0; i < count; ++i)
280         {
281             BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex (i);
282             if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID)
283             {
284                 Breakpoint *bp = target->GetBreakpointByID (cur_bp_id.GetBreakpointID()).get();
285                 BreakpointOptions *bp_options = NULL;
286                 if (cur_bp_id.GetLocationID() == LLDB_INVALID_BREAK_ID)
287                 {
288                     // This breakpoint does not have an associated location.
289                     bp_options = bp->GetOptions();
290                 }
291                 else
292                 {
293                     BreakpointLocationSP bp_loc_sp(bp->FindLocationByID (cur_bp_id.GetLocationID()));
294                     // This breakpoint does have an associated location.
295                     // Get its breakpoint options.
296                     if (bp_loc_sp)
297                         bp_options = bp_loc_sp->GetLocationOptions();
298                 }
299 
300                 // Skip this breakpoiont if bp_options is not good.
301                 if (bp_options == NULL) continue;
302 
303                 // If we are using script language, get the script interpreter
304                 // in order to set or collect command callback.  Otherwise, call
305                 // the methods associated with this object.
306                 if (m_options.m_use_script_language)
307                 {
308                     // Special handling for one-liner specified inline.
309                     if (m_options.m_use_one_liner)
310                         m_interpreter.GetScriptInterpreter()->SetBreakpointCommandCallback (bp_options,
311                                                                                             m_options.m_one_liner.c_str());
312                     else
313                         m_interpreter.GetScriptInterpreter()->CollectDataForBreakpointCommandCallback (bp_options,
314                                                                                                        result);
315                 }
316                 else
317                 {
318                     // Special handling for one-liner specified inline.
319                     if (m_options.m_use_one_liner)
320                         SetBreakpointCommandCallback (bp_options,
321                                                       m_options.m_one_liner.c_str());
322                     else
323                         CollectDataForBreakpointCommandCallback (bp_options,
324                                                                  result);
325                 }
326             }
327         }
328     }
329 
330     return result.Succeeded();
331 }
332 
333 Options *
334 CommandObjectBreakpointCommandAdd::GetOptions ()
335 {
336     return &m_options;
337 }
338 
339 const char *g_reader_instructions = "Enter your debugger command(s).  Type 'DONE' to end.";
340 
341 void
342 CommandObjectBreakpointCommandAdd::CollectDataForBreakpointCommandCallback
343 (
344     BreakpointOptions *bp_options,
345     CommandReturnObject &result
346 )
347 {
348     InputReaderSP reader_sp (new InputReader(m_interpreter.GetDebugger()));
349     std::auto_ptr<BreakpointOptions::CommandData> data_ap(new BreakpointOptions::CommandData());
350     if (reader_sp && data_ap.get())
351     {
352         BatonSP baton_sp (new BreakpointOptions::CommandBaton (data_ap.release()));
353         bp_options->SetCallback (CommandObjectBreakpointCommand::BreakpointOptionsCallbackFunction, baton_sp);
354 
355         Error err (reader_sp->Initialize (CommandObjectBreakpointCommandAdd::GenerateBreakpointCommandCallback,
356                                           bp_options,                   // baton
357                                           eInputReaderGranularityLine,  // token size, to pass to callback function
358                                           "DONE",                       // end token
359                                           "> ",                         // prompt
360                                           true));                       // echo input
361         if (err.Success())
362         {
363             m_interpreter.GetDebugger().PushInputReader (reader_sp);
364             result.SetStatus (eReturnStatusSuccessFinishNoResult);
365         }
366         else
367         {
368             result.AppendError (err.AsCString());
369             result.SetStatus (eReturnStatusFailed);
370         }
371     }
372     else
373     {
374         result.AppendError("out of memory");
375         result.SetStatus (eReturnStatusFailed);
376     }
377 
378 }
379 
380 // Set a one-liner as the callback for the breakpoint.
381 void
382 CommandObjectBreakpointCommandAdd::SetBreakpointCommandCallback (BreakpointOptions *bp_options,
383                                                                  const char *oneliner)
384 {
385     std::auto_ptr<BreakpointOptions::CommandData> data_ap(new BreakpointOptions::CommandData());
386 
387     // It's necessary to set both user_source and script_source to the oneliner.
388     // The former is used to generate callback description (as in breakpoint command list)
389     // while the latter is used for Python to interpret during the actual callback.
390     data_ap->user_source.AppendString (oneliner);
391     data_ap->script_source.AppendString (oneliner);
392 
393     BatonSP baton_sp (new BreakpointOptions::CommandBaton (data_ap.release()));
394     bp_options->SetCallback (CommandObjectBreakpointCommand::BreakpointOptionsCallbackFunction, baton_sp);
395 
396     return;
397 }
398 
399 size_t
400 CommandObjectBreakpointCommandAdd::GenerateBreakpointCommandCallback
401 (
402     void *baton,
403     InputReader &reader,
404     lldb::InputReaderAction notification,
405     const char *bytes,
406     size_t bytes_len
407 )
408 {
409     FILE *out_fh = reader.GetDebugger().GetOutputFileHandle();
410 
411     switch (notification)
412     {
413     case eInputReaderActivate:
414         if (out_fh)
415         {
416             ::fprintf (out_fh, "%s\n", g_reader_instructions);
417             if (reader.GetPrompt())
418                 ::fprintf (out_fh, "%s", reader.GetPrompt());
419         }
420         break;
421 
422     case eInputReaderDeactivate:
423         break;
424 
425     case eInputReaderReactivate:
426         if (out_fh && reader.GetPrompt())
427             ::fprintf (out_fh, "%s", reader.GetPrompt());
428         break;
429 
430     case eInputReaderGotToken:
431         if (bytes && bytes_len && baton)
432         {
433             BreakpointOptions *bp_options = (BreakpointOptions *) baton;
434             if (bp_options)
435             {
436                 Baton *bp_options_baton = bp_options->GetBaton();
437                 if (bp_options_baton)
438                     ((BreakpointOptions::CommandData *)bp_options_baton->m_data)->user_source.AppendString (bytes, bytes_len);
439             }
440         }
441         if (out_fh && !reader.IsDone() && reader.GetPrompt())
442             ::fprintf (out_fh, "%s", reader.GetPrompt());
443         break;
444 
445     case eInputReaderDone:
446         break;
447     }
448 
449     return bytes_len;
450 }
451 
452 
453 //-------------------------------------------------------------------------
454 // CommandObjectBreakpointCommandRemove
455 //-------------------------------------------------------------------------
456 
457 CommandObjectBreakpointCommandRemove::CommandObjectBreakpointCommandRemove (CommandInterpreter &interpreter) :
458     CommandObject (interpreter,
459                    "remove",
460                    "Remove the set of commands from a breakpoint.",
461                    "breakpoint command remove <breakpoint-id>")
462 {
463 }
464 
465 CommandObjectBreakpointCommandRemove::~CommandObjectBreakpointCommandRemove ()
466 {
467 }
468 
469 bool
470 CommandObjectBreakpointCommandRemove::Execute
471 (
472     Args& command,
473     CommandReturnObject &result
474 )
475 {
476     Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
477 
478     if (target == NULL)
479     {
480         result.AppendError ("There is not a current executable; there are no breakpoints from which to remove commands");
481         result.SetStatus (eReturnStatusFailed);
482         return false;
483     }
484 
485     const BreakpointList &breakpoints = target->GetBreakpointList();
486     size_t num_breakpoints = breakpoints.GetSize();
487 
488     if (num_breakpoints == 0)
489     {
490         result.AppendError ("No breakpoints exist to have commands removed");
491         result.SetStatus (eReturnStatusFailed);
492         return false;
493     }
494 
495     if (command.GetArgumentCount() == 0)
496     {
497         result.AppendError ("No breakpoint specified from which to remove the commands");
498         result.SetStatus (eReturnStatusFailed);
499         return false;
500     }
501 
502     BreakpointIDList valid_bp_ids;
503     CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs (command, target, result, &valid_bp_ids);
504 
505     if (result.Succeeded())
506     {
507         const size_t count = valid_bp_ids.GetSize();
508         for (size_t i = 0; i < count; ++i)
509         {
510             BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex (i);
511             if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID)
512             {
513                 Breakpoint *bp = target->GetBreakpointByID (cur_bp_id.GetBreakpointID()).get();
514                 if (cur_bp_id.GetLocationID() != LLDB_INVALID_BREAK_ID)
515                 {
516                     BreakpointLocationSP bp_loc_sp (bp->FindLocationByID (cur_bp_id.GetLocationID()));
517                     if (bp_loc_sp)
518                         bp_loc_sp->ClearCallback();
519                     else
520                     {
521                         result.AppendErrorWithFormat("Invalid breakpoint ID: %u.%u.\n",
522                                                      cur_bp_id.GetBreakpointID(),
523                                                      cur_bp_id.GetLocationID());
524                         result.SetStatus (eReturnStatusFailed);
525                         return false;
526                     }
527                 }
528                 else
529                 {
530                     bp->ClearCallback();
531                 }
532             }
533         }
534     }
535     return result.Succeeded();
536 }
537 
538 
539 //-------------------------------------------------------------------------
540 // CommandObjectBreakpointCommandList
541 //-------------------------------------------------------------------------
542 
543 CommandObjectBreakpointCommandList::CommandObjectBreakpointCommandList (CommandInterpreter &interpreter) :
544     CommandObject (interpreter,
545                    "list",
546                    "List the script or set of commands to be executed when the breakpoint is hit.",
547                    "breakpoint command list <breakpoint-id>")
548 {
549 }
550 
551 CommandObjectBreakpointCommandList::~CommandObjectBreakpointCommandList ()
552 {
553 }
554 
555 bool
556 CommandObjectBreakpointCommandList::Execute
557 (
558     Args& command,
559     CommandReturnObject &result
560 )
561 {
562     Target *target = m_interpreter.GetDebugger().GetSelectedTarget().get();
563 
564     if (target == NULL)
565     {
566         result.AppendError ("There is not a current executable; there are no breakpoints for which to list commands");
567         result.SetStatus (eReturnStatusFailed);
568         return false;
569     }
570 
571     const BreakpointList &breakpoints = target->GetBreakpointList();
572     size_t num_breakpoints = breakpoints.GetSize();
573 
574     if (num_breakpoints == 0)
575     {
576         result.AppendError ("No breakpoints exist for which to list commands");
577         result.SetStatus (eReturnStatusFailed);
578         return false;
579     }
580 
581     if (command.GetArgumentCount() == 0)
582     {
583         result.AppendError ("No breakpoint specified for which to list the commands");
584         result.SetStatus (eReturnStatusFailed);
585         return false;
586     }
587 
588     BreakpointIDList valid_bp_ids;
589     CommandObjectMultiwordBreakpoint::VerifyBreakpointIDs (command, target, result, &valid_bp_ids);
590 
591     if (result.Succeeded())
592     {
593         const size_t count = valid_bp_ids.GetSize();
594         for (size_t i = 0; i < count; ++i)
595         {
596             BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex (i);
597             if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID)
598             {
599                 Breakpoint *bp = target->GetBreakpointByID (cur_bp_id.GetBreakpointID()).get();
600 
601                 if (bp)
602                 {
603                     const BreakpointOptions *bp_options = NULL;
604                     if (cur_bp_id.GetLocationID() != LLDB_INVALID_BREAK_ID)
605                     {
606                         BreakpointLocationSP bp_loc_sp(bp->FindLocationByID (cur_bp_id.GetLocationID()));
607                         if (bp_loc_sp)
608                             bp_options = bp_loc_sp->GetOptionsNoCreate();
609                         else
610                         {
611                             result.AppendErrorWithFormat("Invalid breakpoint ID: %u.%u.\n",
612                                                          cur_bp_id.GetBreakpointID(),
613                                                          cur_bp_id.GetLocationID());
614                             result.SetStatus (eReturnStatusFailed);
615                             return false;
616                         }
617                     }
618                     else
619                     {
620                         bp_options = bp->GetOptions();
621                     }
622 
623                     if (bp_options)
624                     {
625                         StreamString id_str;
626                         BreakpointID::GetCanonicalReference (&id_str, cur_bp_id.GetBreakpointID(), cur_bp_id.GetLocationID());
627                         const Baton *baton = bp_options->GetBaton();
628                         if (baton)
629                         {
630                             result.GetOutputStream().Printf ("Breakpoint %s:\n", id_str.GetData());
631                             result.GetOutputStream().IndentMore ();
632                             baton->GetDescription(&result.GetOutputStream(), eDescriptionLevelFull);
633                             result.GetOutputStream().IndentLess ();
634                         }
635                         else
636                         {
637                             result.AppendMessageWithFormat ("Breakpoint %s does not have an associated command.\n", id_str.GetData());
638                         }
639                     }
640                     result.SetStatus (eReturnStatusSuccessFinishResult);
641                 }
642                 else
643                 {
644                     result.AppendErrorWithFormat("Invalid breakpoint ID: %u.\n", cur_bp_id.GetBreakpointID());
645                     result.SetStatus (eReturnStatusFailed);
646                 }
647 
648             }
649         }
650     }
651 
652     return result.Succeeded();
653 }
654 
655 //-------------------------------------------------------------------------
656 // CommandObjectBreakpointCommand
657 //-------------------------------------------------------------------------
658 
659 CommandObjectBreakpointCommand::CommandObjectBreakpointCommand (CommandInterpreter &interpreter) :
660     CommandObjectMultiword (interpreter,
661                             "command",
662                             "A set of commands for adding, removing and examining bits of code to be executed when the breakpoint is hit (breakpoint 'commmands').",
663                             "command <sub-command> [<sub-command-options>] <breakpoint-id>")
664 {
665     bool status;
666     CommandObjectSP add_command_object (new CommandObjectBreakpointCommandAdd (interpreter));
667     CommandObjectSP remove_command_object (new CommandObjectBreakpointCommandRemove (interpreter));
668     CommandObjectSP list_command_object (new CommandObjectBreakpointCommandList (interpreter));
669 
670     add_command_object->SetCommandName ("breakpoint command add");
671     remove_command_object->SetCommandName ("breakpoint command remove");
672     list_command_object->SetCommandName ("breakpoint command list");
673 
674     status = LoadSubCommand ("add",    add_command_object);
675     status = LoadSubCommand ("remove", remove_command_object);
676     status = LoadSubCommand ("list",   list_command_object);
677 }
678 
679 
680 CommandObjectBreakpointCommand::~CommandObjectBreakpointCommand ()
681 {
682 }
683 
684 bool
685 CommandObjectBreakpointCommand::BreakpointOptionsCallbackFunction
686 (
687     void *baton,
688     StoppointCallbackContext *context,
689     lldb::user_id_t break_id,
690     lldb::user_id_t break_loc_id
691 )
692 {
693     bool ret_value = true;
694     if (baton == NULL)
695         return true;
696 
697 
698     BreakpointOptions::CommandData *data = (BreakpointOptions::CommandData *) baton;
699     StringList &commands = data->user_source;
700 
701     if (commands.GetSize() > 0)
702     {
703         uint32_t num_commands = commands.GetSize();
704         CommandReturnObject result;
705         if (context->exe_ctx.target)
706         {
707 
708             Debugger &debugger = context->exe_ctx.target->GetDebugger();
709             CommandInterpreter &interpreter = debugger.GetCommandInterpreter();
710 
711             FILE *out_fh = debugger.GetOutputFileHandle();
712             FILE *err_fh = debugger.GetErrorFileHandle();
713 
714             uint32_t i;
715             for (i = 0; i < num_commands; ++i)
716             {
717 
718                 // First time through we use the context from the stoppoint, after that we use whatever
719                 // has been set by the previous command.
720 
721                 if (!interpreter.HandleCommand (commands.GetStringAtIndex(i), false, result, &context->exe_ctx))
722                     break;
723 
724                 // FIXME: This isn't really the right way to do this.  We should be able to peek at the public
725                 // to see if there is any new events, but that is racey, since the internal process thread has to run and
726                 // deliver the event to the public queue before a run will show up.  So for now we check
727                 // the internal thread state.
728 
729                 lldb::StateType internal_state = context->exe_ctx.process->GetPrivateState();
730                 if (internal_state != eStateStopped)
731                 {
732                     if (i < num_commands - 1)
733                     {
734                         if (out_fh)
735                             ::fprintf (out_fh, "Short-circuiting command execution because target state changed to %s."
736                                                " last command: \"%s\"\n", StateAsCString(internal_state),
737                                                commands.GetStringAtIndex(i));
738                     }
739                     break;
740                 }
741 
742                 if (out_fh)
743                     ::fprintf (out_fh, "%s", result.GetErrorStream().GetData());
744                 if (err_fh)
745                     ::fprintf (err_fh, "%s", result.GetOutputStream().GetData());
746                 result.Clear();
747                 result.SetStatus (eReturnStatusSuccessFinishNoResult);
748             }
749 
750             if (err_fh && !result.Succeeded() && i < num_commands)
751                 ::fprintf (err_fh, "Attempt to execute '%s' failed.\n", commands.GetStringAtIndex(i));
752 
753             if (out_fh)
754                 ::fprintf (out_fh, "%s", result.GetErrorStream().GetData());
755 
756             if (err_fh)
757                 ::fprintf (err_fh, "%s", result.GetOutputStream().GetData());
758         }
759     }
760     return ret_value;
761 }
762 
763