1 //===-- BreakpointOptions.cpp -----------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 #include "lldb/Breakpoint/BreakpointOptions.h"
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"
21 #include "llvm/ADT/STLExtras.h"
24 using namespace lldb_private;
27 *BreakpointOptions::CommandData::g_option_names[static_cast<uint32_t>(
28 BreakpointOptions::CommandData::OptionNames::LastOptionName)]{
29 "UserSource", "ScriptSource", "StopOnError"};
31 StructuredData::ObjectSP
32 BreakpointOptions::CommandData::SerializeToStructuredData() {
33 size_t num_strings = user_source.GetSize();
34 if (num_strings == 0 && script_source.empty()) {
35 // We shouldn't serialize commands if there aren't any, return an empty sp
37 return StructuredData::ObjectSP();
40 StructuredData::DictionarySP options_dict_sp(
41 new StructuredData::Dictionary());
42 options_dict_sp->AddBooleanItem(GetKey(OptionNames::StopOnError),
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);
53 options_dict_sp->AddStringItem(
54 GetKey(OptionNames::Interpreter),
55 ScriptInterpreter::LanguageToString(interpreter));
56 return options_dict_sp;
59 std::unique_ptr<BreakpointOptions::CommandData>
60 BreakpointOptions::CommandData::CreateFromStructuredData(
61 const StructuredData::Dictionary &options_dict, Status &error) {
62 std::unique_ptr<CommandData> data_up(new CommandData());
63 bool found_something = false;
65 bool success = options_dict.GetValueForKeyAsBoolean(
66 GetKey(OptionNames::StopOnError), data_up->stop_on_error);
69 found_something = true;
71 llvm::StringRef interpreter_str;
72 ScriptLanguage interp_language;
73 success = options_dict.GetValueForKeyAsString(
74 GetKey(OptionNames::Interpreter), interpreter_str);
77 error.SetErrorString("Missing command language value.");
81 found_something = true;
82 interp_language = ScriptInterpreter::StringToLanguage(interpreter_str);
83 if (interp_language == eScriptLanguageUnknown) {
84 error.SetErrorStringWithFormatv("Unknown breakpoint command language: {0}.",
88 data_up->interpreter = interp_language;
90 StructuredData::Array *user_source;
91 success = options_dict.GetValueForKeyAsArray(GetKey(OptionNames::UserSource),
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);
100 data_up->user_source.AppendString(elem_string);
107 return std::unique_ptr<BreakpointOptions::CommandData>();
110 const char *BreakpointOptions::g_option_names[(
111 size_t)BreakpointOptions::OptionNames::LastOptionName]{
112 "ConditionText", "IgnoreCount",
113 "EnabledState", "OneShotState", "AutoContinue"};
115 bool BreakpointOptions::NullCallback(void *baton,
116 StoppointCallbackContext *context,
117 lldb::user_id_t break_id,
118 lldb::user_id_t break_loc_id) {
122 // BreakpointOptions constructor
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),
130 m_set_flags.Set(~((Flags::ValueType)0));
133 BreakpointOptions::BreakpointOptions(const char *condition, bool enabled,
134 int32_t ignore, bool one_shot,
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)
141 m_set_flags.Set(eEnabled | eIgnoreCount | eOneShot
143 if (condition && *condition != '\0') {
144 SetCondition(condition);
148 // BreakpointOptions copy constructor
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.reset(new ThreadSpec(*rhs.m_thread_spec_up));
158 m_condition_text = rhs.m_condition_text;
159 m_condition_text_hash = rhs.m_condition_text_hash;
162 // BreakpointOptions assignment operator
163 const BreakpointOptions &BreakpointOptions::
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.reset(new 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;
181 void BreakpointOptions::CopyOverSetOptions(const BreakpointOptions &incoming)
183 if (incoming.m_set_flags.Test(eEnabled))
185 m_enabled = incoming.m_enabled;
186 m_set_flags.Set(eEnabled);
188 if (incoming.m_set_flags.Test(eOneShot))
190 m_one_shot = incoming.m_one_shot;
191 m_set_flags.Set(eOneShot);
193 if (incoming.m_set_flags.Test(eCallback))
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);
201 if (incoming.m_set_flags.Test(eIgnoreCount))
203 m_ignore_count = incoming.m_ignore_count;
204 m_set_flags.Set(eIgnoreCount);
206 if (incoming.m_set_flags.Test(eCondition))
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);
214 m_condition_text = incoming.m_condition_text;
215 m_condition_text_hash = incoming.m_condition_text_hash;
216 m_set_flags.Set(eCondition);
219 if (incoming.m_set_flags.Test(eAutoContinue))
221 m_auto_continue = incoming.m_auto_continue;
222 m_set_flags.Set(eAutoContinue);
224 if (incoming.m_set_flags.Test(eThreadSpec) && incoming.m_thread_spec_up) {
225 if (!m_thread_spec_up)
226 m_thread_spec_up.reset(new ThreadSpec(*incoming.m_thread_spec_up));
228 *m_thread_spec_up = *incoming.m_thread_spec_up;
229 m_set_flags.Set(eThreadSpec);
234 BreakpointOptions::~BreakpointOptions() = default;
236 std::unique_ptr<BreakpointOptions> BreakpointOptions::CreateFromStructuredData(
237 Target &target, const StructuredData::Dictionary &options_dict,
240 bool one_shot = false;
241 bool auto_continue = false;
242 int32_t ignore_count = 0;
243 llvm::StringRef condition_ref("");
246 const char *key = GetKey(OptionNames::EnabledState);
248 if (key && options_dict.HasKey(key)) {
249 success = options_dict.GetValueForKeyAsBoolean(key, enabled);
251 error.SetErrorStringWithFormat("%s key is not a boolean.", key);
254 set_options.Set(eEnabled);
257 key = GetKey(OptionNames::OneShotState);
258 if (key && options_dict.HasKey(key)) {
259 success = options_dict.GetValueForKeyAsBoolean(key, one_shot);
261 error.SetErrorStringWithFormat("%s key is not a boolean.", key);
264 set_options.Set(eOneShot);
267 key = GetKey(OptionNames::AutoContinue);
268 if (key && options_dict.HasKey(key)) {
269 success = options_dict.GetValueForKeyAsBoolean(key, auto_continue);
271 error.SetErrorStringWithFormat("%s key is not a boolean.", key);
274 set_options.Set(eAutoContinue);
277 key = GetKey(OptionNames::IgnoreCount);
278 if (key && options_dict.HasKey(key)) {
279 success = options_dict.GetValueForKeyAsInteger(key, ignore_count);
281 error.SetErrorStringWithFormat("%s key is not an integer.", key);
284 set_options.Set(eIgnoreCount);
287 key = GetKey(OptionNames::ConditionText);
288 if (key && options_dict.HasKey(key)) {
289 success = options_dict.GetValueForKeyAsString(key, condition_ref);
291 error.SetErrorStringWithFormat("%s key is not an string.", key);
294 set_options.Set(eCondition);
297 std::unique_ptr<CommandData> cmd_data_up;
298 StructuredData::Dictionary *cmds_dict;
299 success = options_dict.GetValueForKeyAsDictionary(
300 CommandData::GetSerializationKey(), cmds_dict);
301 if (success && cmds_dict) {
303 cmd_data_up = CommandData::CreateFromStructuredData(*cmds_dict, cmds_error);
304 if (cmds_error.Fail()) {
305 error.SetErrorStringWithFormat(
306 "Failed to deserialize breakpoint command options: %s.",
307 cmds_error.AsCString());
312 auto bp_options = llvm::make_unique<BreakpointOptions>(
313 condition_ref.str().c_str(), enabled,
314 ignore_count, one_shot, auto_continue);
316 if (cmd_data_up->interpreter == eScriptLanguageNone)
317 bp_options->SetCommandDataCallback(cmd_data_up);
319 ScriptInterpreter *interp = target.GetDebugger().GetScriptInterpreter();
321 error.SetErrorStringWithFormat(
322 "Can't set script commands - no script interpreter");
325 if (interp->GetLanguage() != cmd_data_up->interpreter) {
326 error.SetErrorStringWithFormat(
327 "Current script language doesn't match breakpoint's language: %s",
328 ScriptInterpreter::LanguageToString(cmd_data_up->interpreter)
334 interp->SetBreakpointCommandCallback(bp_options.get(), cmd_data_up);
335 if (script_error.Fail()) {
336 error.SetErrorStringWithFormat("Error generating script callback: %s.",
343 StructuredData::Dictionary *thread_spec_dict;
344 success = options_dict.GetValueForKeyAsDictionary(
345 ThreadSpec::GetSerializationKey(), thread_spec_dict);
347 Status thread_spec_error;
348 std::unique_ptr<ThreadSpec> thread_spec_up =
349 ThreadSpec::CreateFromStructuredData(*thread_spec_dict,
351 if (thread_spec_error.Fail()) {
352 error.SetErrorStringWithFormat(
353 "Failed to deserialize breakpoint thread spec options: %s.",
354 thread_spec_error.AsCString());
357 bp_options->SetThreadSpec(thread_spec_up);
362 StructuredData::ObjectSP BreakpointOptions::SerializeToStructuredData() {
363 StructuredData::DictionarySP options_dict_sp(
364 new StructuredData::Dictionary());
365 if (m_set_flags.Test(eEnabled))
366 options_dict_sp->AddBooleanItem(GetKey(OptionNames::EnabledState),
368 if (m_set_flags.Test(eOneShot))
369 options_dict_sp->AddBooleanItem(GetKey(OptionNames::OneShotState),
371 if (m_set_flags.Test(eAutoContinue))
372 options_dict_sp->AddBooleanItem(GetKey(OptionNames::AutoContinue),
374 if (m_set_flags.Test(eIgnoreCount))
375 options_dict_sp->AddIntegerItem(GetKey(OptionNames::IgnoreCount),
377 if (m_set_flags.Test(eCondition))
378 options_dict_sp->AddStringItem(GetKey(OptionNames::ConditionText),
381 if (m_set_flags.Test(eCallback) && m_baton_is_command_baton) {
383 std::static_pointer_cast<CommandBaton>(m_callback_baton_sp);
384 StructuredData::ObjectSP commands_sp =
385 cmd_baton->getItem()->SerializeToStructuredData();
387 options_dict_sp->AddItem(
388 BreakpointOptions::CommandData::GetSerializationKey(), commands_sp);
391 if (m_set_flags.Test(eThreadSpec) && m_thread_spec_up) {
392 StructuredData::ObjectSP thread_spec_sp =
393 m_thread_spec_up->SerializeToStructuredData();
394 options_dict_sp->AddItem(ThreadSpec::GetSerializationKey(), thread_spec_sp);
397 return options_dict_sp;
401 void BreakpointOptions::SetCallback(BreakpointHitCallback callback,
402 const lldb::BatonSP &callback_baton_sp,
403 bool callback_is_synchronous) {
404 // FIXME: This seems unsafe. If BatonSP actually *is* a CommandBaton, but
405 // in a shared_ptr<Baton> instead of a shared_ptr<CommandBaton>, then we will
406 // set m_baton_is_command_baton to false, which is incorrect. One possible
407 // solution is to make the base Baton class provide a method such as:
408 // virtual StringRef getBatonId() const { return ""; }
409 // and have CommandBaton override this to return something unique, and then
410 // check for it here. Another option might be to make Baton using the llvm
411 // casting infrastructure, so that we could write something like:
412 // if (llvm::isa<CommandBaton>(callback_baton_sp))
413 // at relevant callsites instead of storing a boolean.
414 m_callback_is_synchronous = callback_is_synchronous;
415 m_callback = callback;
416 m_callback_baton_sp = callback_baton_sp;
417 m_baton_is_command_baton = false;
418 m_set_flags.Set(eCallback);
421 void BreakpointOptions::SetCallback(
422 BreakpointHitCallback callback,
423 const BreakpointOptions::CommandBatonSP &callback_baton_sp,
424 bool callback_is_synchronous) {
425 m_callback_is_synchronous = callback_is_synchronous;
426 m_callback = callback;
427 m_callback_baton_sp = callback_baton_sp;
428 m_baton_is_command_baton = true;
429 m_set_flags.Set(eCallback);
432 void BreakpointOptions::ClearCallback() {
433 m_callback = BreakpointOptions::NullCallback;
434 m_callback_is_synchronous = false;
435 m_callback_baton_sp.reset();
436 m_baton_is_command_baton = false;
437 m_set_flags.Clear(eCallback);
440 Baton *BreakpointOptions::GetBaton() { return m_callback_baton_sp.get(); }
442 const Baton *BreakpointOptions::GetBaton() const {
443 return m_callback_baton_sp.get();
446 bool BreakpointOptions::InvokeCallback(StoppointCallbackContext *context,
447 lldb::user_id_t break_id,
448 lldb::user_id_t break_loc_id) {
450 if (context->is_synchronous == IsCallbackSynchronous()) {
451 return m_callback(m_callback_baton_sp ? m_callback_baton_sp->data()
453 context, break_id, break_loc_id);
454 } else if (IsCallbackSynchronous()) {
455 // If a synchronous callback is called at async time, it should not say
463 bool BreakpointOptions::HasCallback() const {
464 return m_callback != BreakpointOptions::NullCallback;
467 bool BreakpointOptions::GetCommandLineCallbacks(StringList &command_list) {
470 if (!m_baton_is_command_baton)
473 auto cmd_baton = std::static_pointer_cast<CommandBaton>(m_callback_baton_sp);
474 CommandData *data = cmd_baton->getItem();
477 command_list = data->user_source;
481 void BreakpointOptions::SetCondition(const char *condition) {
482 if (!condition || condition[0] == '\0') {
484 m_set_flags.Clear(eCondition);
487 m_set_flags.Set(eCondition);
489 m_condition_text.assign(condition);
490 std::hash<std::string> hasher;
491 m_condition_text_hash = hasher(m_condition_text);
494 const char *BreakpointOptions::GetConditionText(size_t *hash) const {
495 if (!m_condition_text.empty()) {
497 *hash = m_condition_text_hash;
499 return m_condition_text.c_str();
505 const ThreadSpec *BreakpointOptions::GetThreadSpecNoCreate() const {
506 return m_thread_spec_up.get();
509 ThreadSpec *BreakpointOptions::GetThreadSpec() {
510 if (m_thread_spec_up == nullptr) {
511 m_set_flags.Set(eThreadSpec);
512 m_thread_spec_up.reset(new ThreadSpec());
515 return m_thread_spec_up.get();
518 void BreakpointOptions::SetThreadID(lldb::tid_t thread_id) {
519 GetThreadSpec()->SetTID(thread_id);
520 m_set_flags.Set(eThreadSpec);
523 void BreakpointOptions::SetThreadSpec(
524 std::unique_ptr<ThreadSpec> &thread_spec_up) {
525 m_thread_spec_up = std::move(thread_spec_up);
526 m_set_flags.Set(eThreadSpec);
529 void BreakpointOptions::GetDescription(Stream *s,
530 lldb::DescriptionLevel level) const {
531 // Figure out if there are any options not at their default value, and only
532 // print anything if there are:
534 if (m_ignore_count != 0 || !m_enabled || m_one_shot || m_auto_continue ||
535 (GetThreadSpecNoCreate() != nullptr &&
536 GetThreadSpecNoCreate()->HasSpecification())) {
537 if (level == lldb::eDescriptionLevelVerbose) {
541 s->PutCString("Breakpoint Options:\n");
545 s->PutCString(" Options: ");
547 if (m_ignore_count > 0)
548 s->Printf("ignore: %d ", m_ignore_count);
549 s->Printf("%sabled ", m_enabled ? "en" : "dis");
552 s->Printf("one-shot ");
555 s->Printf("auto-continue ");
557 if (m_thread_spec_up)
558 m_thread_spec_up->GetDescription(s, level);
560 if (level == lldb::eDescriptionLevelFull) {
566 if (m_callback_baton_sp.get()) {
567 if (level != eDescriptionLevelBrief) {
569 m_callback_baton_sp->GetDescription(s, level);
572 if (!m_condition_text.empty()) {
573 if (level != eDescriptionLevelBrief) {
575 s->Printf("Condition: %s\n", m_condition_text.c_str());
580 void BreakpointOptions::CommandBaton::GetDescription(
581 Stream *s, lldb::DescriptionLevel level) const {
582 const CommandData *data = getItem();
584 if (level == eDescriptionLevelBrief) {
585 s->Printf(", commands = %s",
586 (data && data->user_source.GetSize() > 0) ? "yes" : "no");
591 s->Indent("Breakpoint commands");
592 if (data->interpreter != eScriptLanguageNone)
593 s->Printf(" (%s):\n",
594 ScriptInterpreter::LanguageToString(data->interpreter).c_str());
596 s->PutCString(":\n");
599 if (data && data->user_source.GetSize() > 0) {
600 const size_t num_strings = data->user_source.GetSize();
601 for (size_t i = 0; i < num_strings; ++i) {
602 s->Indent(data->user_source.GetStringAtIndex(i));
606 s->PutCString("No commands.\n");
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);
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)
627 CommandData *data = (CommandData *)baton;
628 StringList &commands = data->user_source;
630 if (commands.GetSize() > 0) {
631 ExecutionContext exe_ctx(context->exe_ctx_ref);
632 Target *target = exe_ctx.GetTargetPtr();
634 CommandReturnObject result;
635 Debugger &debugger = target->GetDebugger();
636 // Rig up the results secondary output stream to the debugger's, so the
637 // 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);
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);
652 debugger.GetCommandInterpreter().HandleCommands(commands, &exe_ctx,
654 result.GetImmediateOutputStream()->Flush();
655 result.GetImmediateErrorStream()->Flush();
661 void BreakpointOptions::Clear()
664 m_thread_spec_up.release();
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;
673 m_condition_text.clear();