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