1 //===-- BreakpointOptions.cpp -----------------------------------*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 #include "lldb/Breakpoint/BreakpointOptions.h"
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"
22 #include "llvm/ADT/STLExtras.h"
25 using namespace lldb_private;
28 *BreakpointOptions::CommandData::g_option_names[static_cast<uint32_t>(
29 BreakpointOptions::CommandData::OptionNames::LastOptionName)]{
30 "UserSource", "ScriptSource", "StopOnError"};
32 StructuredData::ObjectSP
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
38 return StructuredData::ObjectSP();
41 StructuredData::DictionarySP options_dict_sp(
42 new StructuredData::Dictionary());
43 options_dict_sp->AddBooleanItem(GetKey(OptionNames::StopOnError),
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);
54 options_dict_sp->AddStringItem(
55 GetKey(OptionNames::Interpreter),
56 ScriptInterpreter::LanguageToString(interpreter));
57 return options_dict_sp;
60 std::unique_ptr<BreakpointOptions::CommandData>
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;
66 bool success = options_dict.GetValueForKeyAsBoolean(
67 GetKey(OptionNames::StopOnError), data_up->stop_on_error);
70 found_something = true;
72 llvm::StringRef interpreter_str;
73 ScriptLanguage interp_language;
74 success = options_dict.GetValueForKeyAsString(
75 GetKey(OptionNames::Interpreter), interpreter_str);
78 error.SetErrorString("Missing command language value.");
82 found_something = true;
83 interp_language = ScriptInterpreter::StringToLanguage(interpreter_str);
84 if (interp_language == eScriptLanguageUnknown) {
85 error.SetErrorStringWithFormatv("Unknown breakpoint command language: {0}.",
89 data_up->interpreter = interp_language;
91 StructuredData::Array *user_source;
92 success = options_dict.GetValueForKeyAsArray(GetKey(OptionNames::UserSource),
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);
101 data_up->user_source.AppendString(elem_string);
108 return std::unique_ptr<BreakpointOptions::CommandData>();
111 const char *BreakpointOptions::g_option_names[(
112 size_t)BreakpointOptions::OptionNames::LastOptionName]{
113 "ConditionText", "IgnoreCount",
114 "EnabledState", "OneShotState", "AutoContinue"};
116 bool BreakpointOptions::NullCallback(void *baton,
117 StoppointCallbackContext *context,
118 lldb::user_id_t break_id,
119 lldb::user_id_t break_loc_id) {
123 //----------------------------------------------------------------------
124 // BreakpointOptions constructor
125 //----------------------------------------------------------------------
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),
133 m_set_flags.Set(~((Flags::ValueType) 0));
136 BreakpointOptions::BreakpointOptions(const char *condition, bool enabled,
137 int32_t ignore, bool one_shot,
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)
144 m_set_flags.Set(eEnabled | eIgnoreCount | eOneShot
146 if (condition && *condition != '\0') {
147 SetCondition(condition);
151 //----------------------------------------------------------------------
152 // BreakpointOptions copy constructor
153 //----------------------------------------------------------------------
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;
168 //----------------------------------------------------------------------
169 // BreakpointOptions assignment operator
170 //----------------------------------------------------------------------
171 const BreakpointOptions &BreakpointOptions::
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;
189 void BreakpointOptions::CopyOverSetOptions(const BreakpointOptions &incoming)
191 if (incoming.m_set_flags.Test(eEnabled))
193 m_enabled = incoming.m_enabled;
194 m_set_flags.Set(eEnabled);
196 if (incoming.m_set_flags.Test(eOneShot))
198 m_one_shot = incoming.m_one_shot;
199 m_set_flags.Set(eOneShot);
201 if (incoming.m_set_flags.Test(eCallback))
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);
209 if (incoming.m_set_flags.Test(eIgnoreCount))
211 m_ignore_count = incoming.m_ignore_count;
212 m_set_flags.Set(eIgnoreCount);
214 if (incoming.m_set_flags.Test(eCondition))
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);
222 m_condition_text = incoming.m_condition_text;
223 m_condition_text_hash = incoming.m_condition_text_hash;
224 m_set_flags.Set(eCondition);
227 if (incoming.m_set_flags.Test(eAutoContinue))
229 m_auto_continue = incoming.m_auto_continue;
230 m_set_flags.Set(eAutoContinue);
232 if (incoming.m_set_flags.Test(eThreadSpec) && incoming.m_thread_spec_ap)
234 if (!m_thread_spec_ap)
235 m_thread_spec_ap.reset(new ThreadSpec(*incoming.m_thread_spec_ap.get()));
237 *m_thread_spec_ap.get() = *incoming.m_thread_spec_ap.get();
238 m_set_flags.Set(eThreadSpec);
242 //----------------------------------------------------------------------
244 //----------------------------------------------------------------------
245 BreakpointOptions::~BreakpointOptions() = default;
247 std::unique_ptr<BreakpointOptions> BreakpointOptions::CreateFromStructuredData(
248 Target &target, const StructuredData::Dictionary &options_dict,
251 bool one_shot = false;
252 bool auto_continue = false;
253 int32_t ignore_count = 0;
254 llvm::StringRef condition_ref("");
257 const char *key = GetKey(OptionNames::EnabledState);
260 success = options_dict.GetValueForKeyAsBoolean(key, enabled);
262 error.SetErrorStringWithFormat("%s key is not a boolean.",
263 GetKey(OptionNames::EnabledState));
266 set_options.Set(eEnabled);
269 key = GetKey(OptionNames::OneShotState);
271 success = options_dict.GetValueForKeyAsBoolean(key, one_shot);
273 error.SetErrorStringWithFormat("%s key is not a boolean.",
274 GetKey(OptionNames::OneShotState));
277 set_options.Set(eOneShot);
280 key = GetKey(OptionNames::AutoContinue);
282 success = options_dict.GetValueForKeyAsBoolean(key, auto_continue);
284 error.SetErrorStringWithFormat("%s key is not a boolean.",
285 GetKey(OptionNames::AutoContinue));
288 set_options.Set(eAutoContinue);
291 key = GetKey(OptionNames::IgnoreCount);
293 success = options_dict.GetValueForKeyAsInteger(key, ignore_count);
295 error.SetErrorStringWithFormat("%s key is not an integer.",
296 GetKey(OptionNames::IgnoreCount));
299 set_options.Set(eIgnoreCount);
302 key = GetKey(OptionNames::ConditionText);
304 success = options_dict.GetValueForKeyAsString(key, condition_ref);
306 error.SetErrorStringWithFormat("%s key is not an string.",
307 GetKey(OptionNames::ConditionText));
310 set_options.Set(eCondition);
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) {
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());
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);
335 ScriptInterpreter *interp =
336 target.GetDebugger().GetCommandInterpreter().GetScriptInterpreter();
338 error.SetErrorStringWithFormat(
339 "Can't set script commands - no script interpreter");
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)
351 interp->SetBreakpointCommandCallback(bp_options.get(), cmd_data_up);
352 if (script_error.Fail()) {
353 error.SetErrorStringWithFormat("Error generating script callback: %s.",
360 StructuredData::Dictionary *thread_spec_dict;
361 success = options_dict.GetValueForKeyAsDictionary(
362 ThreadSpec::GetSerializationKey(), thread_spec_dict);
364 Status thread_spec_error;
365 std::unique_ptr<ThreadSpec> thread_spec_up =
366 ThreadSpec::CreateFromStructuredData(*thread_spec_dict,
368 if (thread_spec_error.Fail()) {
369 error.SetErrorStringWithFormat(
370 "Failed to deserialize breakpoint thread spec options: %s.",
371 thread_spec_error.AsCString());
374 bp_options->SetThreadSpec(thread_spec_up);
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),
385 if (m_set_flags.Test(eOneShot))
386 options_dict_sp->AddBooleanItem(GetKey(OptionNames::OneShotState),
388 if (m_set_flags.Test(eAutoContinue))
389 options_dict_sp->AddBooleanItem(GetKey(OptionNames::AutoContinue),
391 if (m_set_flags.Test(eIgnoreCount))
392 options_dict_sp->AddIntegerItem(GetKey(OptionNames::IgnoreCount),
394 if (m_set_flags.Test(eCondition))
395 options_dict_sp->AddStringItem(GetKey(OptionNames::ConditionText),
398 if (m_set_flags.Test(eCallback) && m_baton_is_command_baton) {
400 std::static_pointer_cast<CommandBaton>(m_callback_baton_sp);
401 StructuredData::ObjectSP commands_sp =
402 cmd_baton->getItem()->SerializeToStructuredData();
404 options_dict_sp->AddItem(
405 BreakpointOptions::CommandData::GetSerializationKey(), commands_sp);
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);
414 return options_dict_sp;
417 //------------------------------------------------------------------
419 //------------------------------------------------------------------
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);
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);
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);
459 Baton *BreakpointOptions::GetBaton() { return m_callback_baton_sp.get(); }
461 const Baton *BreakpointOptions::GetBaton() const {
462 return m_callback_baton_sp.get();
465 bool BreakpointOptions::InvokeCallback(StoppointCallbackContext *context,
466 lldb::user_id_t break_id,
467 lldb::user_id_t break_loc_id) {
469 if (context->is_synchronous == IsCallbackSynchronous()) {
470 return m_callback(m_callback_baton_sp ? m_callback_baton_sp->data()
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
482 bool BreakpointOptions::HasCallback() const {
483 return m_callback != BreakpointOptions::NullCallback;
486 bool BreakpointOptions::GetCommandLineCallbacks(StringList &command_list) {
489 if (!m_baton_is_command_baton)
492 auto cmd_baton = std::static_pointer_cast<CommandBaton>(m_callback_baton_sp);
493 CommandData *data = cmd_baton->getItem();
496 command_list = data->user_source;
500 void BreakpointOptions::SetCondition(const char *condition) {
501 if (!condition || condition[0] == '\0') {
503 m_set_flags.Clear(eCondition);
506 m_set_flags.Set(eCondition);
508 m_condition_text.assign(condition);
509 std::hash<std::string> hasher;
510 m_condition_text_hash = hasher(m_condition_text);
513 const char *BreakpointOptions::GetConditionText(size_t *hash) const {
514 if (!m_condition_text.empty()) {
516 *hash = m_condition_text_hash;
518 return m_condition_text.c_str();
524 const ThreadSpec *BreakpointOptions::GetThreadSpecNoCreate() const {
525 return m_thread_spec_ap.get();
528 ThreadSpec *BreakpointOptions::GetThreadSpec() {
529 if (m_thread_spec_ap.get() == nullptr)
531 m_set_flags.Set(eThreadSpec);
532 m_thread_spec_ap.reset(new ThreadSpec());
535 return m_thread_spec_ap.get();
538 void BreakpointOptions::SetThreadID(lldb::tid_t thread_id) {
539 GetThreadSpec()->SetTID(thread_id);
540 m_set_flags.Set(eThreadSpec);
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);
549 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:
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) {
561 s->PutCString("Breakpoint Options:\n");
565 s->PutCString(" Options: ");
567 if (m_ignore_count > 0)
568 s->Printf("ignore: %d ", m_ignore_count);
569 s->Printf("%sabled ", m_enabled ? "en" : "dis");
572 s->Printf("one-shot ");
575 s->Printf("auto-continue ");
577 if (m_thread_spec_ap.get())
578 m_thread_spec_ap->GetDescription(s, level);
580 if (level == lldb::eDescriptionLevelFull) {
586 if (m_callback_baton_sp.get()) {
587 if (level != eDescriptionLevelBrief) {
589 m_callback_baton_sp->GetDescription(s, level);
592 if (!m_condition_text.empty()) {
593 if (level != eDescriptionLevelBrief) {
595 s->Printf("Condition: %s\n", m_condition_text.c_str());
600 void BreakpointOptions::CommandBaton::GetDescription(
601 Stream *s, lldb::DescriptionLevel level) const {
602 const CommandData *data = getItem();
604 if (level == eDescriptionLevelBrief) {
605 s->Printf(", commands = %s",
606 (data && data->user_source.GetSize() > 0) ? "yes" : "no");
611 s->Indent("Breakpoint commands");
612 if (data->interpreter != eScriptLanguageNone)
613 s->Printf(" (%s):\n",
614 ScriptInterpreter::LanguageToString(data->interpreter).c_str());
616 s->PutCString(":\n");
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));
626 s->PutCString("No commands.\n");
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);
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)
647 CommandData *data = (CommandData *)baton;
648 StringList &commands = data->user_source;
650 if (commands.GetSize() > 0) {
651 ExecutionContext exe_ctx(context->exe_ctx_ref);
652 Target *target = exe_ctx.GetTargetPtr();
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.
659 StreamSP output_stream(debugger.GetAsyncOutputStream());
660 StreamSP error_stream(debugger.GetAsyncErrorStream());
661 result.SetImmediateOutputStream(output_stream);
662 result.SetImmediateErrorStream(error_stream);
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);
671 debugger.GetCommandInterpreter().HandleCommands(commands, &exe_ctx,
673 result.GetImmediateOutputStream()->Flush();
674 result.GetImmediateErrorStream()->Flush();
680 void BreakpointOptions::Clear()
683 m_thread_spec_ap.release();
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;
692 m_condition_text.clear();