1//===-- CommandObjectBreakpointCommand.cpp --------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "CommandObjectBreakpointCommand.h"
10#include "CommandObjectBreakpoint.h"
11#include "lldb/Breakpoint/Breakpoint.h"
12#include "lldb/Breakpoint/BreakpointIDList.h"
13#include "lldb/Breakpoint/BreakpointLocation.h"
14#include "lldb/Core/IOHandler.h"
15#include "lldb/Host/OptionParser.h"
16#include "lldb/Interpreter/CommandInterpreter.h"
17#include "lldb/Interpreter/CommandReturnObject.h"
18#include "lldb/Interpreter/OptionArgParser.h"
19#include "lldb/Interpreter/OptionGroupPythonClassWithDict.h"
20#include "lldb/Target/Target.h"
21
22using namespace lldb;
23using namespace lldb_private;
24
25// FIXME: "script-type" needs to have its contents determined dynamically, so
26// somebody can add a new scripting language to lldb and have it pickable here
27// without having to change this enumeration by hand and rebuild lldb proper.
28static constexpr OptionEnumValueElement g_script_option_enumeration[] = {
29    {
30        eScriptLanguageNone,
31        "command",
32        "Commands are in the lldb command interpreter language",
33    },
34    {
35        eScriptLanguagePython,
36        "python",
37        "Commands are in the Python language.",
38    },
39    {
40        eScriptLanguageLua,
41        "lua",
42        "Commands are in the Lua language.",
43    },
44    {
45        eScriptLanguageDefault,
46        "default-script",
47        "Commands are in the default scripting language.",
48    },
49};
50
51static constexpr OptionEnumValues ScriptOptionEnum() {
52  return OptionEnumValues(g_script_option_enumeration);
53}
54
55#define LLDB_OPTIONS_breakpoint_command_add
56#include "CommandOptions.inc"
57
58class CommandObjectBreakpointCommandAdd : public CommandObjectParsed,
59                                          public IOHandlerDelegateMultiline {
60public:
61  CommandObjectBreakpointCommandAdd(CommandInterpreter &interpreter)
62      : CommandObjectParsed(interpreter, "add",
63                            "Add LLDB commands to a breakpoint, to be executed "
64                            "whenever the breakpoint is hit."
65                            "  If no breakpoint is specified, adds the "
66                            "commands to the last created breakpoint.",
67                            nullptr),
68        IOHandlerDelegateMultiline("DONE",
69                                   IOHandlerDelegate::Completion::LLDBCommand),
70        m_options(), m_func_options("breakpoint command", false, 'F') {
71    SetHelpLong(
72        R"(
73General information about entering breakpoint commands
74------------------------------------------------------
75
76)"
77        "This command will prompt for commands to be executed when the specified \
78breakpoint is hit.  Each command is typed on its own line following the '> ' \
79prompt until 'DONE' is entered."
80        R"(
81
82)"
83        "Syntactic errors may not be detected when initially entered, and many \
84malformed commands can silently fail when executed.  If your breakpoint commands \
85do not appear to be executing, double-check the command syntax."
86        R"(
87
88)"
89        "Note: You may enter any debugger command exactly as you would at the debugger \
90prompt.  There is no limit to the number of commands supplied, but do NOT enter \
91more than one command per line."
92        R"(
93
94Special information about PYTHON breakpoint commands
95----------------------------------------------------
96
97)"
98        "You may enter either one or more lines of Python, including function \
99definitions or calls to functions that will have been imported by the time \
100the code executes.  Single line breakpoint commands will be interpreted 'as is' \
101when the breakpoint is hit.  Multiple lines of Python will be wrapped in a \
102generated function, and a call to the function will be attached to the breakpoint."
103        R"(
104
105This auto-generated function is passed in three arguments:
106
107    frame:  an lldb.SBFrame object for the frame which hit breakpoint.
108
109    bp_loc: an lldb.SBBreakpointLocation object that represents the breakpoint location that was hit.
110
111    dict:   the python session dictionary hit.
112
113)"
114        "When specifying a python function with the --python-function option, you need \
115to supply the function name prepended by the module name:"
116        R"(
117
118    --python-function myutils.breakpoint_callback
119
120The function itself must have the following prototype:
121
122def breakpoint_callback(frame, bp_loc, dict):
123  # Your code goes here
124
125)"
126        "The arguments are the same as the arguments passed to generated functions as \
127described above.  Note that the global variable 'lldb.frame' will NOT be updated when \
128this function is called, so be sure to use the 'frame' argument. The 'frame' argument \
129can get you to the thread via frame.GetThread(), the thread can get you to the \
130process via thread.GetProcess(), and the process can get you back to the target \
131via process.GetTarget()."
132        R"(
133
134)"
135        "Important Note: As Python code gets collected into functions, access to global \
136variables requires explicit scoping using the 'global' keyword.  Be sure to use correct \
137Python syntax, including indentation, when entering Python breakpoint commands."
138        R"(
139
140Example Python one-line breakpoint command:
141
142(lldb) breakpoint command add -s python 1
143Enter your Python command(s). Type 'DONE' to end.
144> print "Hit this breakpoint!"
145> DONE
146
147As a convenience, this also works for a short Python one-liner:
148
149(lldb) breakpoint command add -s python 1 -o 'import time; print time.asctime()'
150(lldb) run
151Launching '.../a.out'  (x86_64)
152(lldb) Fri Sep 10 12:17:45 2010
153Process 21778 Stopped
154* thread #1: tid = 0x2e03, 0x0000000100000de8 a.out`c + 7 at main.c:39, stop reason = breakpoint 1.1, queue = com.apple.main-thread
155  36
156  37   	int c(int val)
157  38   	{
158  39 ->	    return val + 3;
159  40   	}
160  41
161  42   	int main (int argc, char const *argv[])
162
163Example multiple line Python breakpoint command:
164
165(lldb) breakpoint command add -s p 1
166Enter your Python command(s). Type 'DONE' to end.
167> global bp_count
168> bp_count = bp_count + 1
169> print "Hit this breakpoint " + repr(bp_count) + " times!"
170> DONE
171
172Example multiple line Python breakpoint command, using function definition:
173
174(lldb) breakpoint command add -s python 1
175Enter your Python command(s). Type 'DONE' to end.
176> def breakpoint_output (bp_no):
177>     out_string = "Hit breakpoint number " + repr (bp_no)
178>     print out_string
179>     return True
180> breakpoint_output (1)
181> DONE
182
183)"
184        "In this case, since there is a reference to a global variable, \
185'bp_count', you will also need to make sure 'bp_count' exists and is \
186initialized:"
187        R"(
188
189(lldb) script
190>>> bp_count = 0
191>>> quit()
192
193)"
194        "Your Python code, however organized, can optionally return a value.  \
195If the returned value is False, that tells LLDB not to stop at the breakpoint \
196to which the code is associated. Returning anything other than False, or even \
197returning None, or even omitting a return statement entirely, will cause \
198LLDB to stop."
199        R"(
200
201)"
202        "Final Note: A warning that no breakpoint command was generated when there \
203are no syntax errors may indicate that a function was declared but never called.");
204
205    m_all_options.Append(&m_options);
206    m_all_options.Append(&m_func_options, LLDB_OPT_SET_2 | LLDB_OPT_SET_3,
207                         LLDB_OPT_SET_2);
208    m_all_options.Finalize();
209
210    CommandArgumentEntry arg;
211    CommandArgumentData bp_id_arg;
212
213    // Define the first (and only) variant of this arg.
214    bp_id_arg.arg_type = eArgTypeBreakpointID;
215    bp_id_arg.arg_repetition = eArgRepeatOptional;
216
217    // There is only one variant this argument could be; put it into the
218    // argument entry.
219    arg.push_back(bp_id_arg);
220
221    // Push the data for the first argument into the m_arguments vector.
222    m_arguments.push_back(arg);
223  }
224
225  ~CommandObjectBreakpointCommandAdd() override = default;
226
227  Options *GetOptions() override { return &m_all_options; }
228
229  void IOHandlerActivated(IOHandler &io_handler, bool interactive) override {
230    StreamFileSP output_sp(io_handler.GetOutputStreamFileSP());
231    if (output_sp && interactive) {
232      output_sp->PutCString(g_reader_instructions);
233      output_sp->Flush();
234    }
235  }
236
237  void IOHandlerInputComplete(IOHandler &io_handler,
238                              std::string &line) override {
239    io_handler.SetIsDone(true);
240
241    std::vector<BreakpointOptions *> *bp_options_vec =
242        (std::vector<BreakpointOptions *> *)io_handler.GetUserData();
243    for (BreakpointOptions *bp_options : *bp_options_vec) {
244      if (!bp_options)
245        continue;
246
247      auto cmd_data = std::make_unique<BreakpointOptions::CommandData>();
248      cmd_data->user_source.SplitIntoLines(line.c_str(), line.size());
249      bp_options->SetCommandDataCallback(cmd_data);
250    }
251  }
252
253  void CollectDataForBreakpointCommandCallback(
254      std::vector<BreakpointOptions *> &bp_options_vec,
255      CommandReturnObject &result) {
256    m_interpreter.GetLLDBCommandsFromIOHandler(
257        "> ",             // Prompt
258        *this,            // IOHandlerDelegate
259        &bp_options_vec); // Baton for the "io_handler" that will be passed back
260                          // into our IOHandlerDelegate functions
261  }
262
263  /// Set a one-liner as the callback for the breakpoint.
264  void
265  SetBreakpointCommandCallback(std::vector<BreakpointOptions *> &bp_options_vec,
266                               const char *oneliner) {
267    for (auto bp_options : bp_options_vec) {
268      auto cmd_data = std::make_unique<BreakpointOptions::CommandData>();
269
270      cmd_data->user_source.AppendString(oneliner);
271      cmd_data->stop_on_error = m_options.m_stop_on_error;
272
273      bp_options->SetCommandDataCallback(cmd_data);
274    }
275  }
276
277  class CommandOptions : public OptionGroup {
278  public:
279    CommandOptions()
280        : OptionGroup(), m_use_commands(false), m_use_script_language(false),
281          m_script_language(eScriptLanguageNone), m_use_one_liner(false),
282          m_one_liner() {}
283
284    ~CommandOptions() override = default;
285
286    Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
287                          ExecutionContext *execution_context) override {
288      Status error;
289      const int short_option =
290          g_breakpoint_command_add_options[option_idx].short_option;
291
292      switch (short_option) {
293      case 'o':
294        m_use_one_liner = true;
295        m_one_liner = std::string(option_arg);
296        break;
297
298      case 's':
299        m_script_language = (lldb::ScriptLanguage)OptionArgParser::ToOptionEnum(
300            option_arg,
301            g_breakpoint_command_add_options[option_idx].enum_values,
302            eScriptLanguageNone, error);
303        switch (m_script_language) {
304        case eScriptLanguagePython:
305        case eScriptLanguageLua:
306          m_use_script_language = true;
307          break;
308        case eScriptLanguageNone:
309        case eScriptLanguageUnknown:
310          m_use_script_language = false;
311          break;
312        }
313        break;
314
315      case 'e': {
316        bool success = false;
317        m_stop_on_error =
318            OptionArgParser::ToBoolean(option_arg, false, &success);
319        if (!success)
320          error.SetErrorStringWithFormat(
321              "invalid value for stop-on-error: \"%s\"",
322              option_arg.str().c_str());
323      } break;
324
325      case 'D':
326        m_use_dummy = true;
327        break;
328
329      default:
330        llvm_unreachable("Unimplemented option");
331      }
332      return error;
333    }
334
335    void OptionParsingStarting(ExecutionContext *execution_context) override {
336      m_use_commands = true;
337      m_use_script_language = false;
338      m_script_language = eScriptLanguageNone;
339
340      m_use_one_liner = false;
341      m_stop_on_error = true;
342      m_one_liner.clear();
343      m_use_dummy = false;
344    }
345
346    llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
347      return llvm::makeArrayRef(g_breakpoint_command_add_options);
348    }
349
350    // Instance variables to hold the values for command options.
351
352    bool m_use_commands;
353    bool m_use_script_language;
354    lldb::ScriptLanguage m_script_language;
355
356    // Instance variables to hold the values for one_liner options.
357    bool m_use_one_liner;
358    std::string m_one_liner;
359    bool m_stop_on_error;
360    bool m_use_dummy;
361  };
362
363protected:
364  bool DoExecute(Args &command, CommandReturnObject &result) override {
365    Target &target = GetSelectedOrDummyTarget(m_options.m_use_dummy);
366
367    const BreakpointList &breakpoints = target.GetBreakpointList();
368    size_t num_breakpoints = breakpoints.GetSize();
369
370    if (num_breakpoints == 0) {
371      result.AppendError("No breakpoints exist to have commands added");
372      result.SetStatus(eReturnStatusFailed);
373      return false;
374    }
375
376    if (!m_func_options.GetName().empty()) {
377      m_options.m_use_one_liner = false;
378      if (!m_options.m_use_script_language) {
379        m_options.m_script_language = GetDebugger().GetScriptLanguage();
380        m_options.m_use_script_language = true;
381      }
382    }
383
384    BreakpointIDList valid_bp_ids;
385    CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs(
386        command, &target, result, &valid_bp_ids,
387        BreakpointName::Permissions::PermissionKinds::listPerm);
388
389    m_bp_options_vec.clear();
390
391    if (result.Succeeded()) {
392      const size_t count = valid_bp_ids.GetSize();
393
394      for (size_t i = 0; i < count; ++i) {
395        BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex(i);
396        if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID) {
397          Breakpoint *bp =
398              target.GetBreakpointByID(cur_bp_id.GetBreakpointID()).get();
399          BreakpointOptions *bp_options = nullptr;
400          if (cur_bp_id.GetLocationID() == LLDB_INVALID_BREAK_ID) {
401            // This breakpoint does not have an associated location.
402            bp_options = bp->GetOptions();
403          } else {
404            BreakpointLocationSP bp_loc_sp(
405                bp->FindLocationByID(cur_bp_id.GetLocationID()));
406            // This breakpoint does have an associated location. Get its
407            // breakpoint options.
408            if (bp_loc_sp)
409              bp_options = bp_loc_sp->GetLocationOptions();
410          }
411          if (bp_options)
412            m_bp_options_vec.push_back(bp_options);
413        }
414      }
415
416      // If we are using script language, get the script interpreter in order
417      // to set or collect command callback.  Otherwise, call the methods
418      // associated with this object.
419      if (m_options.m_use_script_language) {
420        ScriptInterpreter *script_interp = GetDebugger().GetScriptInterpreter(
421            /*can_create=*/true, m_options.m_script_language);
422        // Special handling for one-liner specified inline.
423        if (m_options.m_use_one_liner) {
424          script_interp->SetBreakpointCommandCallback(
425              m_bp_options_vec, m_options.m_one_liner.c_str());
426        } else if (!m_func_options.GetName().empty()) {
427          Status error = script_interp->SetBreakpointCommandCallbackFunction(
428              m_bp_options_vec, m_func_options.GetName().c_str(),
429              m_func_options.GetStructuredData());
430          if (!error.Success())
431            result.SetError(error);
432        } else {
433          script_interp->CollectDataForBreakpointCommandCallback(
434              m_bp_options_vec, result);
435        }
436      } else {
437        // Special handling for one-liner specified inline.
438        if (m_options.m_use_one_liner)
439          SetBreakpointCommandCallback(m_bp_options_vec,
440                                       m_options.m_one_liner.c_str());
441        else
442          CollectDataForBreakpointCommandCallback(m_bp_options_vec, result);
443      }
444    }
445
446    return result.Succeeded();
447  }
448
449private:
450  CommandOptions m_options;
451  OptionGroupPythonClassWithDict m_func_options;
452  OptionGroupOptions m_all_options;
453
454  std::vector<BreakpointOptions *> m_bp_options_vec; // This stores the
455                                                     // breakpoint options that
456                                                     // we are currently
457  // collecting commands for.  In the CollectData... calls we need to hand this
458  // off to the IOHandler, which may run asynchronously. So we have to have
459  // some way to keep it alive, and not leak it. Making it an ivar of the
460  // command object, which never goes away achieves this.  Note that if we were
461  // able to run the same command concurrently in one interpreter we'd have to
462  // make this "per invocation".  But there are many more reasons why it is not
463  // in general safe to do that in lldb at present, so it isn't worthwhile to
464  // come up with a more complex mechanism to address this particular weakness
465  // right now.
466  static const char *g_reader_instructions;
467};
468
469const char *CommandObjectBreakpointCommandAdd::g_reader_instructions =
470    "Enter your debugger command(s).  Type 'DONE' to end.\n";
471
472// CommandObjectBreakpointCommandDelete
473
474#define LLDB_OPTIONS_breakpoint_command_delete
475#include "CommandOptions.inc"
476
477class CommandObjectBreakpointCommandDelete : public CommandObjectParsed {
478public:
479  CommandObjectBreakpointCommandDelete(CommandInterpreter &interpreter)
480      : CommandObjectParsed(interpreter, "delete",
481                            "Delete the set of commands from a breakpoint.",
482                            nullptr),
483        m_options() {
484    CommandArgumentEntry arg;
485    CommandArgumentData bp_id_arg;
486
487    // Define the first (and only) variant of this arg.
488    bp_id_arg.arg_type = eArgTypeBreakpointID;
489    bp_id_arg.arg_repetition = eArgRepeatPlain;
490
491    // There is only one variant this argument could be; put it into the
492    // argument entry.
493    arg.push_back(bp_id_arg);
494
495    // Push the data for the first argument into the m_arguments vector.
496    m_arguments.push_back(arg);
497  }
498
499  ~CommandObjectBreakpointCommandDelete() override = default;
500
501  Options *GetOptions() override { return &m_options; }
502
503  class CommandOptions : public Options {
504  public:
505    CommandOptions() : Options(), m_use_dummy(false) {}
506
507    ~CommandOptions() override = default;
508
509    Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg,
510                          ExecutionContext *execution_context) override {
511      Status error;
512      const int short_option = m_getopt_table[option_idx].val;
513
514      switch (short_option) {
515      case 'D':
516        m_use_dummy = true;
517        break;
518
519      default:
520        llvm_unreachable("Unimplemented option");
521      }
522
523      return error;
524    }
525
526    void OptionParsingStarting(ExecutionContext *execution_context) override {
527      m_use_dummy = false;
528    }
529
530    llvm::ArrayRef<OptionDefinition> GetDefinitions() override {
531      return llvm::makeArrayRef(g_breakpoint_command_delete_options);
532    }
533
534    // Instance variables to hold the values for command options.
535    bool m_use_dummy;
536  };
537
538protected:
539  bool DoExecute(Args &command, CommandReturnObject &result) override {
540    Target &target = GetSelectedOrDummyTarget(m_options.m_use_dummy);
541
542    const BreakpointList &breakpoints = target.GetBreakpointList();
543    size_t num_breakpoints = breakpoints.GetSize();
544
545    if (num_breakpoints == 0) {
546      result.AppendError("No breakpoints exist to have commands deleted");
547      result.SetStatus(eReturnStatusFailed);
548      return false;
549    }
550
551    if (command.empty()) {
552      result.AppendError(
553          "No breakpoint specified from which to delete the commands");
554      result.SetStatus(eReturnStatusFailed);
555      return false;
556    }
557
558    BreakpointIDList valid_bp_ids;
559    CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs(
560        command, &target, result, &valid_bp_ids,
561        BreakpointName::Permissions::PermissionKinds::listPerm);
562
563    if (result.Succeeded()) {
564      const size_t count = valid_bp_ids.GetSize();
565      for (size_t i = 0; i < count; ++i) {
566        BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex(i);
567        if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID) {
568          Breakpoint *bp =
569              target.GetBreakpointByID(cur_bp_id.GetBreakpointID()).get();
570          if (cur_bp_id.GetLocationID() != LLDB_INVALID_BREAK_ID) {
571            BreakpointLocationSP bp_loc_sp(
572                bp->FindLocationByID(cur_bp_id.GetLocationID()));
573            if (bp_loc_sp)
574              bp_loc_sp->ClearCallback();
575            else {
576              result.AppendErrorWithFormat("Invalid breakpoint ID: %u.%u.\n",
577                                           cur_bp_id.GetBreakpointID(),
578                                           cur_bp_id.GetLocationID());
579              result.SetStatus(eReturnStatusFailed);
580              return false;
581            }
582          } else {
583            bp->ClearCallback();
584          }
585        }
586      }
587    }
588    return result.Succeeded();
589  }
590
591private:
592  CommandOptions m_options;
593};
594
595// CommandObjectBreakpointCommandList
596
597class CommandObjectBreakpointCommandList : public CommandObjectParsed {
598public:
599  CommandObjectBreakpointCommandList(CommandInterpreter &interpreter)
600      : CommandObjectParsed(interpreter, "list",
601                            "List the script or set of commands to be "
602                            "executed when the breakpoint is hit.",
603                            nullptr, eCommandRequiresTarget) {
604    CommandArgumentEntry arg;
605    CommandArgumentData bp_id_arg;
606
607    // Define the first (and only) variant of this arg.
608    bp_id_arg.arg_type = eArgTypeBreakpointID;
609    bp_id_arg.arg_repetition = eArgRepeatPlain;
610
611    // There is only one variant this argument could be; put it into the
612    // argument entry.
613    arg.push_back(bp_id_arg);
614
615    // Push the data for the first argument into the m_arguments vector.
616    m_arguments.push_back(arg);
617  }
618
619  ~CommandObjectBreakpointCommandList() override = default;
620
621protected:
622  bool DoExecute(Args &command, CommandReturnObject &result) override {
623    Target *target = &GetSelectedTarget();
624
625    const BreakpointList &breakpoints = target->GetBreakpointList();
626    size_t num_breakpoints = breakpoints.GetSize();
627
628    if (num_breakpoints == 0) {
629      result.AppendError("No breakpoints exist for which to list commands");
630      result.SetStatus(eReturnStatusFailed);
631      return false;
632    }
633
634    if (command.empty()) {
635      result.AppendError(
636          "No breakpoint specified for which to list the commands");
637      result.SetStatus(eReturnStatusFailed);
638      return false;
639    }
640
641    BreakpointIDList valid_bp_ids;
642    CommandObjectMultiwordBreakpoint::VerifyBreakpointOrLocationIDs(
643        command, target, result, &valid_bp_ids,
644        BreakpointName::Permissions::PermissionKinds::listPerm);
645
646    if (result.Succeeded()) {
647      const size_t count = valid_bp_ids.GetSize();
648      for (size_t i = 0; i < count; ++i) {
649        BreakpointID cur_bp_id = valid_bp_ids.GetBreakpointIDAtIndex(i);
650        if (cur_bp_id.GetBreakpointID() != LLDB_INVALID_BREAK_ID) {
651          Breakpoint *bp =
652              target->GetBreakpointByID(cur_bp_id.GetBreakpointID()).get();
653
654          if (bp) {
655            BreakpointLocationSP bp_loc_sp;
656            if (cur_bp_id.GetLocationID() != LLDB_INVALID_BREAK_ID) {
657              bp_loc_sp = bp->FindLocationByID(cur_bp_id.GetLocationID());
658              if (!bp_loc_sp) {
659                result.AppendErrorWithFormat("Invalid breakpoint ID: %u.%u.\n",
660                                             cur_bp_id.GetBreakpointID(),
661                                             cur_bp_id.GetLocationID());
662                result.SetStatus(eReturnStatusFailed);
663                return false;
664              }
665            }
666
667            StreamString id_str;
668            BreakpointID::GetCanonicalReference(&id_str,
669                                                cur_bp_id.GetBreakpointID(),
670                                                cur_bp_id.GetLocationID());
671            const Baton *baton = nullptr;
672            if (bp_loc_sp)
673              baton =
674                  bp_loc_sp
675                      ->GetOptionsSpecifyingKind(BreakpointOptions::eCallback)
676                      ->GetBaton();
677            else
678              baton = bp->GetOptions()->GetBaton();
679
680            if (baton) {
681              result.GetOutputStream().Printf("Breakpoint %s:\n",
682                                              id_str.GetData());
683              baton->GetDescription(result.GetOutputStream().AsRawOstream(),
684                                    eDescriptionLevelFull,
685                                    result.GetOutputStream().GetIndentLevel() +
686                                        2);
687            } else {
688              result.AppendMessageWithFormat(
689                  "Breakpoint %s does not have an associated command.\n",
690                  id_str.GetData());
691            }
692          }
693          result.SetStatus(eReturnStatusSuccessFinishResult);
694        } else {
695          result.AppendErrorWithFormat("Invalid breakpoint ID: %u.\n",
696                                       cur_bp_id.GetBreakpointID());
697          result.SetStatus(eReturnStatusFailed);
698        }
699      }
700    }
701
702    return result.Succeeded();
703  }
704};
705
706// CommandObjectBreakpointCommand
707
708CommandObjectBreakpointCommand::CommandObjectBreakpointCommand(
709    CommandInterpreter &interpreter)
710    : CommandObjectMultiword(
711          interpreter, "command",
712          "Commands for adding, removing and listing "
713          "LLDB commands executed when a breakpoint is "
714          "hit.",
715          "command <sub-command> [<sub-command-options>] <breakpoint-id>") {
716  CommandObjectSP add_command_object(
717      new CommandObjectBreakpointCommandAdd(interpreter));
718  CommandObjectSP delete_command_object(
719      new CommandObjectBreakpointCommandDelete(interpreter));
720  CommandObjectSP list_command_object(
721      new CommandObjectBreakpointCommandList(interpreter));
722
723  add_command_object->SetCommandName("breakpoint command add");
724  delete_command_object->SetCommandName("breakpoint command delete");
725  list_command_object->SetCommandName("breakpoint command list");
726
727  LoadSubCommand("add", add_command_object);
728  LoadSubCommand("delete", delete_command_object);
729  LoadSubCommand("list", list_command_object);
730}
731
732CommandObjectBreakpointCommand::~CommandObjectBreakpointCommand() = default;
733