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