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