1 //===-- BreakpointOptions.cpp -----------------------------------*- C++ -*-===//
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 "lldb/Breakpoint/BreakpointOptions.h"
10 
11 #include "lldb/Breakpoint/StoppointCallbackContext.h"
12 #include "lldb/Core/Value.h"
13 #include "lldb/Interpreter/CommandInterpreter.h"
14 #include "lldb/Interpreter/CommandReturnObject.h"
15 #include "lldb/Target/Process.h"
16 #include "lldb/Target/Target.h"
17 #include "lldb/Target/ThreadSpec.h"
18 #include "lldb/Utility/Stream.h"
19 #include "lldb/Utility/StringList.h"
20 
21 #include "llvm/ADT/STLExtras.h"
22 
23 using namespace lldb;
24 using namespace lldb_private;
25 
26 const char
27     *BreakpointOptions::CommandData::g_option_names[static_cast<uint32_t>(
28         BreakpointOptions::CommandData::OptionNames::LastOptionName)]{
29         "UserSource", "ScriptSource", "StopOnError"};
30 
31 StructuredData::ObjectSP
32 BreakpointOptions::CommandData::SerializeToStructuredData() {
33   size_t num_strings = user_source.GetSize();
34   if (num_strings == 0 && script_source.empty()) {
35     // We shouldn't serialize commands if there aren't any, return an empty sp
36     // to indicate this.
37     return StructuredData::ObjectSP();
38   }
39 
40   StructuredData::DictionarySP options_dict_sp(
41       new StructuredData::Dictionary());
42   options_dict_sp->AddBooleanItem(GetKey(OptionNames::StopOnError),
43                                   stop_on_error);
44 
45   StructuredData::ArraySP user_source_sp(new StructuredData::Array());
46   for (size_t i = 0; i < num_strings; i++) {
47     StructuredData::StringSP item_sp(
48         new StructuredData::String(user_source[i]));
49     user_source_sp->AddItem(item_sp);
50     options_dict_sp->AddItem(GetKey(OptionNames::UserSource), user_source_sp);
51   }
52 
53   options_dict_sp->AddStringItem(
54       GetKey(OptionNames::Interpreter),
55       ScriptInterpreter::LanguageToString(interpreter));
56   return options_dict_sp;
57 }
58 
59 std::unique_ptr<BreakpointOptions::CommandData>
60 BreakpointOptions::CommandData::CreateFromStructuredData(
61     const StructuredData::Dictionary &options_dict, Status &error) {
62   std::unique_ptr<CommandData> data_up(new CommandData());
63   bool found_something = false;
64 
65   bool success = options_dict.GetValueForKeyAsBoolean(
66       GetKey(OptionNames::StopOnError), data_up->stop_on_error);
67 
68   if (success)
69     found_something = true;
70 
71   llvm::StringRef interpreter_str;
72   ScriptLanguage interp_language;
73   success = options_dict.GetValueForKeyAsString(
74       GetKey(OptionNames::Interpreter), interpreter_str);
75 
76   if (!success) {
77     error.SetErrorString("Missing command language value.");
78     return data_up;
79   }
80 
81   found_something = true;
82   interp_language = ScriptInterpreter::StringToLanguage(interpreter_str);
83   if (interp_language == eScriptLanguageUnknown) {
84     error.SetErrorStringWithFormatv("Unknown breakpoint command language: {0}.",
85                                     interpreter_str);
86     return data_up;
87   }
88   data_up->interpreter = interp_language;
89 
90   StructuredData::Array *user_source;
91   success = options_dict.GetValueForKeyAsArray(GetKey(OptionNames::UserSource),
92                                                user_source);
93   if (success) {
94     found_something = true;
95     size_t num_elems = user_source->GetSize();
96     for (size_t i = 0; i < num_elems; i++) {
97       llvm::StringRef elem_string;
98       success = user_source->GetItemAtIndexAsString(i, elem_string);
99       if (success)
100         data_up->user_source.AppendString(elem_string);
101     }
102   }
103 
104   if (found_something)
105     return data_up;
106   else
107     return std::unique_ptr<BreakpointOptions::CommandData>();
108 }
109 
110 const char *BreakpointOptions::g_option_names[(
111     size_t)BreakpointOptions::OptionNames::LastOptionName]{
112     "ConditionText", "IgnoreCount",
113     "EnabledState", "OneShotState", "AutoContinue"};
114 
115 bool BreakpointOptions::NullCallback(void *baton,
116                                      StoppointCallbackContext *context,
117                                      lldb::user_id_t break_id,
118                                      lldb::user_id_t break_loc_id) {
119   return true;
120 }
121 
122 //----------------------------------------------------------------------
123 // BreakpointOptions constructor
124 //----------------------------------------------------------------------
125 BreakpointOptions::BreakpointOptions(bool all_flags_set)
126     : m_callback(BreakpointOptions::NullCallback), m_callback_baton_sp(),
127       m_baton_is_command_baton(false), m_callback_is_synchronous(false),
128       m_enabled(true), m_one_shot(false), m_ignore_count(0), m_thread_spec_ap(),
129       m_condition_text(), m_condition_text_hash(0), m_auto_continue(false),
130       m_set_flags(0) {
131         if (all_flags_set)
132           m_set_flags.Set(~((Flags::ValueType) 0));
133       }
134 
135 BreakpointOptions::BreakpointOptions(const char *condition, bool enabled,
136                                      int32_t ignore, bool one_shot,
137                                      bool auto_continue)
138     : m_callback(nullptr), m_baton_is_command_baton(false),
139       m_callback_is_synchronous(false), m_enabled(enabled),
140       m_one_shot(one_shot), m_ignore_count(ignore),
141       m_condition_text_hash(0), m_auto_continue(auto_continue)
142 {
143     m_set_flags.Set(eEnabled | eIgnoreCount | eOneShot
144                    | eAutoContinue);
145     if (condition && *condition != '\0') {
146       SetCondition(condition);
147     }
148 }
149 
150 //----------------------------------------------------------------------
151 // BreakpointOptions copy constructor
152 //----------------------------------------------------------------------
153 BreakpointOptions::BreakpointOptions(const BreakpointOptions &rhs)
154     : m_callback(rhs.m_callback), m_callback_baton_sp(rhs.m_callback_baton_sp),
155       m_baton_is_command_baton(rhs.m_baton_is_command_baton),
156       m_callback_is_synchronous(rhs.m_callback_is_synchronous),
157       m_enabled(rhs.m_enabled), m_one_shot(rhs.m_one_shot),
158       m_ignore_count(rhs.m_ignore_count), m_thread_spec_ap(),
159       m_auto_continue(rhs.m_auto_continue),
160       m_set_flags(rhs.m_set_flags) {
161   if (rhs.m_thread_spec_ap != nullptr)
162     m_thread_spec_ap.reset(new ThreadSpec(*rhs.m_thread_spec_ap));
163   m_condition_text = rhs.m_condition_text;
164   m_condition_text_hash = rhs.m_condition_text_hash;
165 }
166 
167 //----------------------------------------------------------------------
168 // BreakpointOptions assignment operator
169 //----------------------------------------------------------------------
170 const BreakpointOptions &BreakpointOptions::
171 operator=(const BreakpointOptions &rhs) {
172   m_callback = rhs.m_callback;
173   m_callback_baton_sp = rhs.m_callback_baton_sp;
174   m_baton_is_command_baton = rhs.m_baton_is_command_baton;
175   m_callback_is_synchronous = rhs.m_callback_is_synchronous;
176   m_enabled = rhs.m_enabled;
177   m_one_shot = rhs.m_one_shot;
178   m_ignore_count = rhs.m_ignore_count;
179   if (rhs.m_thread_spec_ap != nullptr)
180     m_thread_spec_ap.reset(new ThreadSpec(*rhs.m_thread_spec_ap));
181   m_condition_text = rhs.m_condition_text;
182   m_condition_text_hash = rhs.m_condition_text_hash;
183   m_auto_continue = rhs.m_auto_continue;
184   m_set_flags = rhs.m_set_flags;
185   return *this;
186 }
187 
188 void BreakpointOptions::CopyOverSetOptions(const BreakpointOptions &incoming)
189 {
190   if (incoming.m_set_flags.Test(eEnabled))
191   {
192     m_enabled = incoming.m_enabled;
193     m_set_flags.Set(eEnabled);
194   }
195   if (incoming.m_set_flags.Test(eOneShot))
196   {
197     m_one_shot = incoming.m_one_shot;
198     m_set_flags.Set(eOneShot);
199   }
200   if (incoming.m_set_flags.Test(eCallback))
201   {
202     m_callback = incoming.m_callback;
203     m_callback_baton_sp = incoming.m_callback_baton_sp;
204     m_callback_is_synchronous = incoming.m_callback_is_synchronous;
205     m_baton_is_command_baton = incoming.m_baton_is_command_baton;
206     m_set_flags.Set(eCallback);
207   }
208   if (incoming.m_set_flags.Test(eIgnoreCount))
209   {
210     m_ignore_count = incoming.m_ignore_count;
211     m_set_flags.Set(eIgnoreCount);
212   }
213   if (incoming.m_set_flags.Test(eCondition))
214   {
215     // If we're copying over an empty condition, mark it as unset.
216     if (incoming.m_condition_text.empty()) {
217       m_condition_text.clear();
218       m_condition_text_hash = 0;
219       m_set_flags.Clear(eCondition);
220     } else {
221       m_condition_text = incoming.m_condition_text;
222       m_condition_text_hash = incoming.m_condition_text_hash;
223       m_set_flags.Set(eCondition);
224     }
225   }
226   if (incoming.m_set_flags.Test(eAutoContinue))
227   {
228     m_auto_continue = incoming.m_auto_continue;
229     m_set_flags.Set(eAutoContinue);
230   }
231   if (incoming.m_set_flags.Test(eThreadSpec) && incoming.m_thread_spec_ap)
232   {
233     if (!m_thread_spec_ap)
234       m_thread_spec_ap.reset(new ThreadSpec(*incoming.m_thread_spec_ap));
235     else
236       *m_thread_spec_ap = *incoming.m_thread_spec_ap;
237     m_set_flags.Set(eThreadSpec);
238   }
239 }
240 
241 //----------------------------------------------------------------------
242 // Destructor
243 //----------------------------------------------------------------------
244 BreakpointOptions::~BreakpointOptions() = default;
245 
246 std::unique_ptr<BreakpointOptions> BreakpointOptions::CreateFromStructuredData(
247     Target &target, const StructuredData::Dictionary &options_dict,
248     Status &error) {
249   bool enabled = true;
250   bool one_shot = false;
251   bool auto_continue = false;
252   int32_t ignore_count = 0;
253   llvm::StringRef condition_ref("");
254   Flags set_options;
255 
256   const char *key = GetKey(OptionNames::EnabledState);
257   bool success;
258   if (key) {
259     success = options_dict.GetValueForKeyAsBoolean(key, enabled);
260     if (!success) {
261       error.SetErrorStringWithFormat("%s key is not a boolean.",
262                                    GetKey(OptionNames::EnabledState));
263       return nullptr;
264     }
265     set_options.Set(eEnabled);
266   }
267 
268   key = GetKey(OptionNames::OneShotState);
269   if (key) {
270     success = options_dict.GetValueForKeyAsBoolean(key, one_shot);
271     if (!success) {
272       error.SetErrorStringWithFormat("%s key is not a boolean.",
273                                      GetKey(OptionNames::OneShotState));
274       return nullptr;
275       }
276       set_options.Set(eOneShot);
277   }
278 
279   key = GetKey(OptionNames::AutoContinue);
280   if (key) {
281     success = options_dict.GetValueForKeyAsBoolean(key, auto_continue);
282     if (!success) {
283       error.SetErrorStringWithFormat("%s key is not a boolean.",
284                                      GetKey(OptionNames::AutoContinue));
285       return nullptr;
286       }
287       set_options.Set(eAutoContinue);
288   }
289 
290   key = GetKey(OptionNames::IgnoreCount);
291   if (key) {
292     success = options_dict.GetValueForKeyAsInteger(key, ignore_count);
293     if (!success) {
294       error.SetErrorStringWithFormat("%s key is not an integer.",
295                                      GetKey(OptionNames::IgnoreCount));
296       return nullptr;
297     }
298     set_options.Set(eIgnoreCount);
299   }
300 
301   key = GetKey(OptionNames::ConditionText);
302   if (key) {
303     success = options_dict.GetValueForKeyAsString(key, condition_ref);
304     if (!success) {
305       error.SetErrorStringWithFormat("%s key is not an string.",
306                                      GetKey(OptionNames::ConditionText));
307       return nullptr;
308     }
309     set_options.Set(eCondition);
310   }
311 
312   std::unique_ptr<CommandData> cmd_data_up;
313   StructuredData::Dictionary *cmds_dict;
314   success = options_dict.GetValueForKeyAsDictionary(
315       CommandData::GetSerializationKey(), cmds_dict);
316   if (success && cmds_dict) {
317     Status cmds_error;
318     cmd_data_up = CommandData::CreateFromStructuredData(*cmds_dict, cmds_error);
319     if (cmds_error.Fail()) {
320       error.SetErrorStringWithFormat(
321           "Failed to deserialize breakpoint command options: %s.",
322           cmds_error.AsCString());
323       return nullptr;
324     }
325   }
326 
327   auto bp_options = llvm::make_unique<BreakpointOptions>(
328       condition_ref.str().c_str(), enabled,
329       ignore_count, one_shot, auto_continue);
330   if (cmd_data_up) {
331     if (cmd_data_up->interpreter == eScriptLanguageNone)
332       bp_options->SetCommandDataCallback(cmd_data_up);
333     else {
334       ScriptInterpreter *interp =
335           target.GetDebugger().GetCommandInterpreter().GetScriptInterpreter();
336       if (!interp) {
337         error.SetErrorStringWithFormat(
338             "Can't set script commands - no script interpreter");
339         return nullptr;
340       }
341       if (interp->GetLanguage() != cmd_data_up->interpreter) {
342         error.SetErrorStringWithFormat(
343             "Current script language doesn't match breakpoint's language: %s",
344             ScriptInterpreter::LanguageToString(cmd_data_up->interpreter)
345                 .c_str());
346         return nullptr;
347       }
348       Status script_error;
349       script_error =
350           interp->SetBreakpointCommandCallback(bp_options.get(), cmd_data_up);
351       if (script_error.Fail()) {
352         error.SetErrorStringWithFormat("Error generating script callback: %s.",
353                                        error.AsCString());
354         return nullptr;
355       }
356     }
357   }
358 
359   StructuredData::Dictionary *thread_spec_dict;
360   success = options_dict.GetValueForKeyAsDictionary(
361       ThreadSpec::GetSerializationKey(), thread_spec_dict);
362   if (success) {
363     Status thread_spec_error;
364     std::unique_ptr<ThreadSpec> thread_spec_up =
365         ThreadSpec::CreateFromStructuredData(*thread_spec_dict,
366                                              thread_spec_error);
367     if (thread_spec_error.Fail()) {
368       error.SetErrorStringWithFormat(
369           "Failed to deserialize breakpoint thread spec options: %s.",
370           thread_spec_error.AsCString());
371       return nullptr;
372     }
373     bp_options->SetThreadSpec(thread_spec_up);
374   }
375   return bp_options;
376 }
377 
378 StructuredData::ObjectSP BreakpointOptions::SerializeToStructuredData() {
379   StructuredData::DictionarySP options_dict_sp(
380       new StructuredData::Dictionary());
381   if (m_set_flags.Test(eEnabled))
382     options_dict_sp->AddBooleanItem(GetKey(OptionNames::EnabledState),
383                                     m_enabled);
384   if (m_set_flags.Test(eOneShot))
385     options_dict_sp->AddBooleanItem(GetKey(OptionNames::OneShotState),
386                                m_one_shot);
387   if (m_set_flags.Test(eAutoContinue))
388     options_dict_sp->AddBooleanItem(GetKey(OptionNames::AutoContinue),
389                                m_auto_continue);
390   if (m_set_flags.Test(eIgnoreCount))
391     options_dict_sp->AddIntegerItem(GetKey(OptionNames::IgnoreCount),
392                                     m_ignore_count);
393   if (m_set_flags.Test(eCondition))
394     options_dict_sp->AddStringItem(GetKey(OptionNames::ConditionText),
395                                    m_condition_text);
396 
397   if (m_set_flags.Test(eCallback) && m_baton_is_command_baton) {
398     auto cmd_baton =
399         std::static_pointer_cast<CommandBaton>(m_callback_baton_sp);
400     StructuredData::ObjectSP commands_sp =
401         cmd_baton->getItem()->SerializeToStructuredData();
402     if (commands_sp) {
403       options_dict_sp->AddItem(
404           BreakpointOptions::CommandData::GetSerializationKey(), commands_sp);
405     }
406   }
407   if (m_set_flags.Test(eThreadSpec) && m_thread_spec_ap) {
408     StructuredData::ObjectSP thread_spec_sp =
409         m_thread_spec_ap->SerializeToStructuredData();
410     options_dict_sp->AddItem(ThreadSpec::GetSerializationKey(), thread_spec_sp);
411   }
412 
413   return options_dict_sp;
414 }
415 
416 //------------------------------------------------------------------
417 // Callbacks
418 //------------------------------------------------------------------
419 void BreakpointOptions::SetCallback(BreakpointHitCallback callback,
420                                     const lldb::BatonSP &callback_baton_sp,
421                                     bool callback_is_synchronous) {
422   // FIXME: This seems unsafe.  If BatonSP actually *is* a CommandBaton, but
423   // in a shared_ptr<Baton> instead of a shared_ptr<CommandBaton>, then we will
424   // set m_baton_is_command_baton to false, which is incorrect. One possible
425   // solution is to make the base Baton class provide a method such as:
426   //     virtual StringRef getBatonId() const { return ""; }
427   // and have CommandBaton override this to return something unique, and then
428   // check for it here.  Another option might be to make Baton using the llvm
429   // casting infrastructure, so that we could write something like:
430   //     if (llvm::isa<CommandBaton>(callback_baton_sp))
431   // at relevant callsites instead of storing a boolean.
432   m_callback_is_synchronous = callback_is_synchronous;
433   m_callback = callback;
434   m_callback_baton_sp = callback_baton_sp;
435   m_baton_is_command_baton = false;
436   m_set_flags.Set(eCallback);
437 }
438 
439 void BreakpointOptions::SetCallback(
440     BreakpointHitCallback callback,
441     const BreakpointOptions::CommandBatonSP &callback_baton_sp,
442     bool callback_is_synchronous) {
443   m_callback_is_synchronous = callback_is_synchronous;
444   m_callback = callback;
445   m_callback_baton_sp = callback_baton_sp;
446   m_baton_is_command_baton = true;
447   m_set_flags.Set(eCallback);
448 }
449 
450 void BreakpointOptions::ClearCallback() {
451   m_callback = BreakpointOptions::NullCallback;
452   m_callback_is_synchronous = false;
453   m_callback_baton_sp.reset();
454   m_baton_is_command_baton = false;
455   m_set_flags.Clear(eCallback);
456 }
457 
458 Baton *BreakpointOptions::GetBaton() { return m_callback_baton_sp.get(); }
459 
460 const Baton *BreakpointOptions::GetBaton() const {
461   return m_callback_baton_sp.get();
462 }
463 
464 bool BreakpointOptions::InvokeCallback(StoppointCallbackContext *context,
465                                        lldb::user_id_t break_id,
466                                        lldb::user_id_t break_loc_id) {
467   if (m_callback) {
468     if (context->is_synchronous == IsCallbackSynchronous()) {
469         return m_callback(m_callback_baton_sp ? m_callback_baton_sp->data()
470                                           : nullptr,
471                       context, break_id, break_loc_id);
472     } else if (IsCallbackSynchronous()) {
473       // If a synchronous callback is called at async time, it should not say
474       // to stop.
475       return false;
476     }
477   }
478   return true;
479 }
480 
481 bool BreakpointOptions::HasCallback() const {
482   return m_callback != BreakpointOptions::NullCallback;
483 }
484 
485 bool BreakpointOptions::GetCommandLineCallbacks(StringList &command_list) {
486   if (!HasCallback())
487     return false;
488   if (!m_baton_is_command_baton)
489     return false;
490 
491   auto cmd_baton = std::static_pointer_cast<CommandBaton>(m_callback_baton_sp);
492   CommandData *data = cmd_baton->getItem();
493   if (!data)
494     return false;
495   command_list = data->user_source;
496   return true;
497 }
498 
499 void BreakpointOptions::SetCondition(const char *condition) {
500   if (!condition || condition[0] == '\0') {
501     condition = "";
502     m_set_flags.Clear(eCondition);
503   }
504   else
505     m_set_flags.Set(eCondition);
506 
507   m_condition_text.assign(condition);
508   std::hash<std::string> hasher;
509   m_condition_text_hash = hasher(m_condition_text);
510 }
511 
512 const char *BreakpointOptions::GetConditionText(size_t *hash) const {
513   if (!m_condition_text.empty()) {
514     if (hash)
515       *hash = m_condition_text_hash;
516 
517     return m_condition_text.c_str();
518   } else {
519     return nullptr;
520   }
521 }
522 
523 const ThreadSpec *BreakpointOptions::GetThreadSpecNoCreate() const {
524   return m_thread_spec_ap.get();
525 }
526 
527 ThreadSpec *BreakpointOptions::GetThreadSpec() {
528   if (m_thread_spec_ap == nullptr) {
529     m_set_flags.Set(eThreadSpec);
530     m_thread_spec_ap.reset(new ThreadSpec());
531   }
532 
533   return m_thread_spec_ap.get();
534 }
535 
536 void BreakpointOptions::SetThreadID(lldb::tid_t thread_id) {
537   GetThreadSpec()->SetTID(thread_id);
538   m_set_flags.Set(eThreadSpec);
539 }
540 
541 void BreakpointOptions::SetThreadSpec(
542     std::unique_ptr<ThreadSpec> &thread_spec_up) {
543   m_thread_spec_ap = std::move(thread_spec_up);
544   m_set_flags.Set(eThreadSpec);
545 }
546 
547 void BreakpointOptions::GetDescription(Stream *s,
548                                        lldb::DescriptionLevel level) const {
549   // Figure out if there are any options not at their default value, and only
550   // print anything if there are:
551 
552   if (m_ignore_count != 0 || !m_enabled || m_one_shot || m_auto_continue ||
553       (GetThreadSpecNoCreate() != nullptr &&
554        GetThreadSpecNoCreate()->HasSpecification())) {
555     if (level == lldb::eDescriptionLevelVerbose) {
556       s->EOL();
557       s->IndentMore();
558       s->Indent();
559       s->PutCString("Breakpoint Options:\n");
560       s->IndentMore();
561       s->Indent();
562     } else
563       s->PutCString(" Options: ");
564 
565     if (m_ignore_count > 0)
566       s->Printf("ignore: %d ", m_ignore_count);
567     s->Printf("%sabled ", m_enabled ? "en" : "dis");
568 
569     if (m_one_shot)
570       s->Printf("one-shot ");
571 
572     if (m_auto_continue)
573       s->Printf("auto-continue ");
574 
575     if (m_thread_spec_ap)
576       m_thread_spec_ap->GetDescription(s, level);
577 
578     if (level == lldb::eDescriptionLevelFull) {
579       s->IndentLess();
580       s->IndentMore();
581     }
582   }
583 
584   if (m_callback_baton_sp.get()) {
585     if (level != eDescriptionLevelBrief) {
586       s->EOL();
587       m_callback_baton_sp->GetDescription(s, level);
588     }
589   }
590   if (!m_condition_text.empty()) {
591     if (level != eDescriptionLevelBrief) {
592       s->EOL();
593       s->Printf("Condition: %s\n", m_condition_text.c_str());
594     }
595   }
596 }
597 
598 void BreakpointOptions::CommandBaton::GetDescription(
599     Stream *s, lldb::DescriptionLevel level) const {
600   const CommandData *data = getItem();
601 
602   if (level == eDescriptionLevelBrief) {
603     s->Printf(", commands = %s",
604               (data && data->user_source.GetSize() > 0) ? "yes" : "no");
605     return;
606   }
607 
608   s->IndentMore();
609   s->Indent("Breakpoint commands");
610   if (data->interpreter != eScriptLanguageNone)
611     s->Printf(" (%s):\n",
612               ScriptInterpreter::LanguageToString(data->interpreter).c_str());
613   else
614     s->PutCString(":\n");
615 
616   s->IndentMore();
617   if (data && data->user_source.GetSize() > 0) {
618     const size_t num_strings = data->user_source.GetSize();
619     for (size_t i = 0; i < num_strings; ++i) {
620       s->Indent(data->user_source.GetStringAtIndex(i));
621       s->EOL();
622     }
623   } else {
624     s->PutCString("No commands.\n");
625   }
626   s->IndentLess();
627   s->IndentLess();
628 }
629 
630 void BreakpointOptions::SetCommandDataCallback(
631     std::unique_ptr<CommandData> &cmd_data) {
632   cmd_data->interpreter = eScriptLanguageNone;
633   auto baton_sp = std::make_shared<CommandBaton>(std::move(cmd_data));
634   SetCallback(BreakpointOptions::BreakpointOptionsCallbackFunction, baton_sp);
635   m_set_flags.Set(eCallback);
636 }
637 
638 bool BreakpointOptions::BreakpointOptionsCallbackFunction(
639     void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id,
640     lldb::user_id_t break_loc_id) {
641   bool ret_value = true;
642   if (baton == nullptr)
643     return true;
644 
645   CommandData *data = (CommandData *)baton;
646   StringList &commands = data->user_source;
647 
648   if (commands.GetSize() > 0) {
649     ExecutionContext exe_ctx(context->exe_ctx_ref);
650     Target *target = exe_ctx.GetTargetPtr();
651     if (target) {
652       CommandReturnObject result;
653       Debugger &debugger = target->GetDebugger();
654       // Rig up the results secondary output stream to the debugger's, so the
655       // output will come out synchronously if the debugger is set up that way.
656 
657       StreamSP output_stream(debugger.GetAsyncOutputStream());
658       StreamSP error_stream(debugger.GetAsyncErrorStream());
659       result.SetImmediateOutputStream(output_stream);
660       result.SetImmediateErrorStream(error_stream);
661 
662       CommandInterpreterRunOptions options;
663       options.SetStopOnContinue(true);
664       options.SetStopOnError(data->stop_on_error);
665       options.SetEchoCommands(true);
666       options.SetPrintResults(true);
667       options.SetAddToHistory(false);
668 
669       debugger.GetCommandInterpreter().HandleCommands(commands, &exe_ctx,
670                                                       options, result);
671       result.GetImmediateOutputStream()->Flush();
672       result.GetImmediateErrorStream()->Flush();
673     }
674   }
675   return ret_value;
676 }
677 
678 void BreakpointOptions::Clear()
679 {
680   m_set_flags.Clear();
681   m_thread_spec_ap.release();
682   m_one_shot = false;
683   m_ignore_count = 0;
684   m_auto_continue = false;
685   m_callback = nullptr;
686   m_callback_baton_sp.reset();
687   m_baton_is_command_baton = false;
688   m_callback_is_synchronous = false;
689   m_enabled = false;
690   m_condition_text.clear();
691 }
692