1 //===-- BreakpointOptions.cpp ---------------------------------------------===//
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());
64 bool success
= options_dict
.GetValueForKeyAsBoolean(
65 GetKey(OptionNames::StopOnError
), data_up
->stop_on_error
);
67 llvm::StringRef interpreter_str
;
68 ScriptLanguage interp_language
;
69 success
= options_dict
.GetValueForKeyAsString(
70 GetKey(OptionNames::Interpreter
), interpreter_str
);
73 error
.SetErrorString("Missing command language value.");
77 interp_language
= ScriptInterpreter::StringToLanguage(interpreter_str
);
78 if (interp_language
== eScriptLanguageUnknown
) {
79 error
.SetErrorStringWithFormatv("Unknown breakpoint command language: {0}.",
83 data_up
->interpreter
= interp_language
;
85 StructuredData::Array
*user_source
;
86 success
= options_dict
.GetValueForKeyAsArray(GetKey(OptionNames::UserSource
),
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
);
100 const char *BreakpointOptions::g_option_names
[(
101 size_t)BreakpointOptions::OptionNames::LastOptionName
]{
102 "ConditionText", "IgnoreCount",
103 "EnabledState", "OneShotState", "AutoContinue"};
105 bool BreakpointOptions::NullCallback(void *baton
,
106 StoppointCallbackContext
*context
,
107 lldb::user_id_t break_id
,
108 lldb::user_id_t break_loc_id
) {
112 // BreakpointOptions constructor
113 BreakpointOptions::BreakpointOptions(bool all_flags_set
)
114 : m_callback(BreakpointOptions::NullCallback
),
115 m_baton_is_command_baton(false), m_callback_is_synchronous(false),
116 m_enabled(true), m_one_shot(false), m_ignore_count(0),
117 m_condition_text_hash(0), m_inject_condition(false),
118 m_auto_continue(false), m_set_flags(0) {
120 m_set_flags
.Set(~((Flags::ValueType
)0));
123 BreakpointOptions::BreakpointOptions(const char *condition
, bool enabled
,
124 int32_t ignore
, bool one_shot
,
126 : m_callback(nullptr), m_baton_is_command_baton(false),
127 m_callback_is_synchronous(false), m_enabled(enabled
),
128 m_one_shot(one_shot
), m_ignore_count(ignore
), m_condition_text_hash(0),
129 m_inject_condition(false), m_auto_continue(auto_continue
) {
130 m_set_flags
.Set(eEnabled
| eIgnoreCount
| eOneShot
| eAutoContinue
);
131 if (condition
&& *condition
!= '\0') {
132 SetCondition(condition
);
136 // BreakpointOptions copy constructor
137 BreakpointOptions::BreakpointOptions(const BreakpointOptions
&rhs
)
138 : m_callback(rhs
.m_callback
), m_callback_baton_sp(rhs
.m_callback_baton_sp
),
139 m_baton_is_command_baton(rhs
.m_baton_is_command_baton
),
140 m_callback_is_synchronous(rhs
.m_callback_is_synchronous
),
141 m_enabled(rhs
.m_enabled
), m_one_shot(rhs
.m_one_shot
),
142 m_ignore_count(rhs
.m_ignore_count
), m_inject_condition(false),
143 m_auto_continue(rhs
.m_auto_continue
), m_set_flags(rhs
.m_set_flags
) {
144 if (rhs
.m_thread_spec_up
!= nullptr)
145 m_thread_spec_up
= std::make_unique
<ThreadSpec
>(*rhs
.m_thread_spec_up
);
146 m_condition_text
= rhs
.m_condition_text
;
147 m_condition_text_hash
= rhs
.m_condition_text_hash
;
150 // BreakpointOptions assignment operator
151 const BreakpointOptions
&BreakpointOptions::
152 operator=(const BreakpointOptions
&rhs
) {
153 m_callback
= rhs
.m_callback
;
154 m_callback_baton_sp
= rhs
.m_callback_baton_sp
;
155 m_baton_is_command_baton
= rhs
.m_baton_is_command_baton
;
156 m_callback_is_synchronous
= rhs
.m_callback_is_synchronous
;
157 m_enabled
= rhs
.m_enabled
;
158 m_one_shot
= rhs
.m_one_shot
;
159 m_ignore_count
= rhs
.m_ignore_count
;
160 if (rhs
.m_thread_spec_up
!= nullptr)
161 m_thread_spec_up
= std::make_unique
<ThreadSpec
>(*rhs
.m_thread_spec_up
);
162 m_condition_text
= rhs
.m_condition_text
;
163 m_condition_text_hash
= rhs
.m_condition_text_hash
;
164 m_inject_condition
= rhs
.m_inject_condition
;
165 m_auto_continue
= rhs
.m_auto_continue
;
166 m_set_flags
= rhs
.m_set_flags
;
170 void BreakpointOptions::CopyOverSetOptions(const BreakpointOptions
&incoming
)
172 if (incoming
.m_set_flags
.Test(eEnabled
))
174 m_enabled
= incoming
.m_enabled
;
175 m_set_flags
.Set(eEnabled
);
177 if (incoming
.m_set_flags
.Test(eOneShot
))
179 m_one_shot
= incoming
.m_one_shot
;
180 m_set_flags
.Set(eOneShot
);
182 if (incoming
.m_set_flags
.Test(eCallback
))
184 m_callback
= incoming
.m_callback
;
185 m_callback_baton_sp
= incoming
.m_callback_baton_sp
;
186 m_callback_is_synchronous
= incoming
.m_callback_is_synchronous
;
187 m_baton_is_command_baton
= incoming
.m_baton_is_command_baton
;
188 m_set_flags
.Set(eCallback
);
190 if (incoming
.m_set_flags
.Test(eIgnoreCount
))
192 m_ignore_count
= incoming
.m_ignore_count
;
193 m_set_flags
.Set(eIgnoreCount
);
195 if (incoming
.m_set_flags
.Test(eCondition
))
197 // If we're copying over an empty condition, mark it as unset.
198 if (incoming
.m_condition_text
.empty()) {
199 m_condition_text
.clear();
200 m_condition_text_hash
= 0;
201 m_set_flags
.Clear(eCondition
);
203 m_condition_text
= incoming
.m_condition_text
;
204 m_condition_text_hash
= incoming
.m_condition_text_hash
;
205 m_set_flags
.Set(eCondition
);
208 if (incoming
.m_set_flags
.Test(eAutoContinue
))
210 m_auto_continue
= incoming
.m_auto_continue
;
211 m_set_flags
.Set(eAutoContinue
);
213 if (incoming
.m_set_flags
.Test(eThreadSpec
) && incoming
.m_thread_spec_up
) {
214 if (!m_thread_spec_up
)
216 std::make_unique
<ThreadSpec
>(*incoming
.m_thread_spec_up
);
218 *m_thread_spec_up
= *incoming
.m_thread_spec_up
;
219 m_set_flags
.Set(eThreadSpec
);
224 BreakpointOptions::~BreakpointOptions() = default;
226 std::unique_ptr
<BreakpointOptions
> BreakpointOptions::CreateFromStructuredData(
227 Target
&target
, const StructuredData::Dictionary
&options_dict
,
230 bool one_shot
= false;
231 bool auto_continue
= false;
232 uint32_t ignore_count
= 0;
233 llvm::StringRef
condition_ref("");
236 const char *key
= GetKey(OptionNames::EnabledState
);
238 if (key
&& options_dict
.HasKey(key
)) {
239 success
= options_dict
.GetValueForKeyAsBoolean(key
, enabled
);
241 error
.SetErrorStringWithFormat("%s key is not a boolean.", key
);
244 set_options
.Set(eEnabled
);
247 key
= GetKey(OptionNames::OneShotState
);
248 if (key
&& options_dict
.HasKey(key
)) {
249 success
= options_dict
.GetValueForKeyAsBoolean(key
, one_shot
);
251 error
.SetErrorStringWithFormat("%s key is not a boolean.", key
);
254 set_options
.Set(eOneShot
);
257 key
= GetKey(OptionNames::AutoContinue
);
258 if (key
&& options_dict
.HasKey(key
)) {
259 success
= options_dict
.GetValueForKeyAsBoolean(key
, auto_continue
);
261 error
.SetErrorStringWithFormat("%s key is not a boolean.", key
);
264 set_options
.Set(eAutoContinue
);
267 key
= GetKey(OptionNames::IgnoreCount
);
268 if (key
&& options_dict
.HasKey(key
)) {
269 success
= options_dict
.GetValueForKeyAsInteger(key
, ignore_count
);
271 error
.SetErrorStringWithFormat("%s key is not an integer.", key
);
274 set_options
.Set(eIgnoreCount
);
277 key
= GetKey(OptionNames::ConditionText
);
278 if (key
&& options_dict
.HasKey(key
)) {
279 success
= options_dict
.GetValueForKeyAsString(key
, condition_ref
);
281 error
.SetErrorStringWithFormat("%s key is not an string.", key
);
284 set_options
.Set(eCondition
);
287 std::unique_ptr
<CommandData
> cmd_data_up
;
288 StructuredData::Dictionary
*cmds_dict
;
289 success
= options_dict
.GetValueForKeyAsDictionary(
290 CommandData::GetSerializationKey(), cmds_dict
);
291 if (success
&& cmds_dict
) {
293 cmd_data_up
= CommandData::CreateFromStructuredData(*cmds_dict
, cmds_error
);
294 if (cmds_error
.Fail()) {
295 error
.SetErrorStringWithFormat(
296 "Failed to deserialize breakpoint command options: %s.",
297 cmds_error
.AsCString());
302 auto bp_options
= std::make_unique
<BreakpointOptions
>(
303 condition_ref
.str().c_str(), enabled
,
304 ignore_count
, one_shot
, auto_continue
);
306 if (cmd_data_up
->interpreter
== eScriptLanguageNone
)
307 bp_options
->SetCommandDataCallback(cmd_data_up
);
309 ScriptInterpreter
*interp
= target
.GetDebugger().GetScriptInterpreter();
311 error
.SetErrorString(
312 "Can't set script commands - no script interpreter");
315 if (interp
->GetLanguage() != cmd_data_up
->interpreter
) {
316 error
.SetErrorStringWithFormat(
317 "Current script language doesn't match breakpoint's language: %s",
318 ScriptInterpreter::LanguageToString(cmd_data_up
->interpreter
)
324 interp
->SetBreakpointCommandCallback(*bp_options
, cmd_data_up
);
325 if (script_error
.Fail()) {
326 error
.SetErrorStringWithFormat("Error generating script callback: %s.",
333 StructuredData::Dictionary
*thread_spec_dict
;
334 success
= options_dict
.GetValueForKeyAsDictionary(
335 ThreadSpec::GetSerializationKey(), thread_spec_dict
);
337 Status thread_spec_error
;
338 std::unique_ptr
<ThreadSpec
> thread_spec_up
=
339 ThreadSpec::CreateFromStructuredData(*thread_spec_dict
,
341 if (thread_spec_error
.Fail()) {
342 error
.SetErrorStringWithFormat(
343 "Failed to deserialize breakpoint thread spec options: %s.",
344 thread_spec_error
.AsCString());
347 bp_options
->SetThreadSpec(thread_spec_up
);
352 StructuredData::ObjectSP
BreakpointOptions::SerializeToStructuredData() {
353 StructuredData::DictionarySP
options_dict_sp(
354 new StructuredData::Dictionary());
355 if (m_set_flags
.Test(eEnabled
))
356 options_dict_sp
->AddBooleanItem(GetKey(OptionNames::EnabledState
),
358 if (m_set_flags
.Test(eOneShot
))
359 options_dict_sp
->AddBooleanItem(GetKey(OptionNames::OneShotState
),
361 if (m_set_flags
.Test(eAutoContinue
))
362 options_dict_sp
->AddBooleanItem(GetKey(OptionNames::AutoContinue
),
364 if (m_set_flags
.Test(eIgnoreCount
))
365 options_dict_sp
->AddIntegerItem(GetKey(OptionNames::IgnoreCount
),
367 if (m_set_flags
.Test(eCondition
))
368 options_dict_sp
->AddStringItem(GetKey(OptionNames::ConditionText
),
371 if (m_set_flags
.Test(eCallback
) && m_baton_is_command_baton
) {
373 std::static_pointer_cast
<CommandBaton
>(m_callback_baton_sp
);
374 StructuredData::ObjectSP commands_sp
=
375 cmd_baton
->getItem()->SerializeToStructuredData();
377 options_dict_sp
->AddItem(
378 BreakpointOptions::CommandData::GetSerializationKey(), commands_sp
);
381 if (m_set_flags
.Test(eThreadSpec
) && m_thread_spec_up
) {
382 StructuredData::ObjectSP thread_spec_sp
=
383 m_thread_spec_up
->SerializeToStructuredData();
384 options_dict_sp
->AddItem(ThreadSpec::GetSerializationKey(), thread_spec_sp
);
387 return options_dict_sp
;
391 void BreakpointOptions::SetCallback(BreakpointHitCallback callback
,
392 const lldb::BatonSP
&callback_baton_sp
,
393 bool callback_is_synchronous
) {
394 // FIXME: This seems unsafe. If BatonSP actually *is* a CommandBaton, but
395 // in a shared_ptr<Baton> instead of a shared_ptr<CommandBaton>, then we will
396 // set m_baton_is_command_baton to false, which is incorrect. One possible
397 // solution is to make the base Baton class provide a method such as:
398 // virtual StringRef getBatonId() const { return ""; }
399 // and have CommandBaton override this to return something unique, and then
400 // check for it here. Another option might be to make Baton using the llvm
401 // casting infrastructure, so that we could write something like:
402 // if (llvm::isa<CommandBaton>(callback_baton_sp))
403 // at relevant callsites instead of storing a boolean.
404 m_callback_is_synchronous
= callback_is_synchronous
;
405 m_callback
= callback
;
406 m_callback_baton_sp
= callback_baton_sp
;
407 m_baton_is_command_baton
= false;
408 m_set_flags
.Set(eCallback
);
411 void BreakpointOptions::SetCallback(
412 BreakpointHitCallback callback
,
413 const BreakpointOptions::CommandBatonSP
&callback_baton_sp
,
414 bool callback_is_synchronous
) {
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
= true;
419 m_set_flags
.Set(eCallback
);
422 void BreakpointOptions::ClearCallback() {
423 m_callback
= BreakpointOptions::NullCallback
;
424 m_callback_is_synchronous
= false;
425 m_callback_baton_sp
.reset();
426 m_baton_is_command_baton
= false;
427 m_set_flags
.Clear(eCallback
);
430 Baton
*BreakpointOptions::GetBaton() { return m_callback_baton_sp
.get(); }
432 const Baton
*BreakpointOptions::GetBaton() const {
433 return m_callback_baton_sp
.get();
436 bool BreakpointOptions::InvokeCallback(StoppointCallbackContext
*context
,
437 lldb::user_id_t break_id
,
438 lldb::user_id_t break_loc_id
) {
440 if (context
->is_synchronous
== IsCallbackSynchronous()) {
441 return m_callback(m_callback_baton_sp
? m_callback_baton_sp
->data()
443 context
, break_id
, break_loc_id
);
444 } else if (IsCallbackSynchronous()) {
451 bool BreakpointOptions::HasCallback() const {
452 return m_callback
!= BreakpointOptions::NullCallback
;
455 bool BreakpointOptions::GetCommandLineCallbacks(StringList
&command_list
) {
458 if (!m_baton_is_command_baton
)
461 auto cmd_baton
= std::static_pointer_cast
<CommandBaton
>(m_callback_baton_sp
);
462 CommandData
*data
= cmd_baton
->getItem();
465 command_list
= data
->user_source
;
469 void BreakpointOptions::SetCondition(const char *condition
) {
470 if (!condition
|| condition
[0] == '\0') {
472 m_set_flags
.Clear(eCondition
);
475 m_set_flags
.Set(eCondition
);
477 m_condition_text
.assign(condition
);
478 std::hash
<std::string
> hasher
;
479 m_condition_text_hash
= hasher(m_condition_text
);
482 const char *BreakpointOptions::GetConditionText(size_t *hash
) const {
483 if (!m_condition_text
.empty()) {
485 *hash
= m_condition_text_hash
;
487 return m_condition_text
.c_str();
493 const ThreadSpec
*BreakpointOptions::GetThreadSpecNoCreate() const {
494 return m_thread_spec_up
.get();
497 ThreadSpec
*BreakpointOptions::GetThreadSpec() {
498 if (m_thread_spec_up
== nullptr) {
499 m_set_flags
.Set(eThreadSpec
);
500 m_thread_spec_up
= std::make_unique
<ThreadSpec
>();
503 return m_thread_spec_up
.get();
506 void BreakpointOptions::SetThreadID(lldb::tid_t thread_id
) {
507 GetThreadSpec()->SetTID(thread_id
);
508 m_set_flags
.Set(eThreadSpec
);
511 void BreakpointOptions::SetThreadSpec(
512 std::unique_ptr
<ThreadSpec
> &thread_spec_up
) {
513 m_thread_spec_up
= std::move(thread_spec_up
);
514 m_set_flags
.Set(eThreadSpec
);
517 void BreakpointOptions::GetDescription(Stream
*s
,
518 lldb::DescriptionLevel level
) const {
519 // Figure out if there are any options not at their default value, and only
520 // print anything if there are:
522 if (m_ignore_count
!= 0 || !m_enabled
|| m_one_shot
|| m_auto_continue
||
523 (GetThreadSpecNoCreate() != nullptr &&
524 GetThreadSpecNoCreate()->HasSpecification())) {
525 if (level
== lldb::eDescriptionLevelVerbose
) {
529 s
->PutCString("Breakpoint Options:\n");
533 s
->PutCString(" Options: ");
535 if (m_ignore_count
> 0)
536 s
->Printf("ignore: %d ", m_ignore_count
);
537 s
->Printf("%sabled ", m_enabled
? "en" : "dis");
540 s
->Printf("one-shot ");
543 s
->Printf("auto-continue ");
545 if (m_thread_spec_up
)
546 m_thread_spec_up
->GetDescription(s
, level
);
548 if (level
== lldb::eDescriptionLevelFull
) {
554 if (m_callback_baton_sp
.get()) {
555 if (level
!= eDescriptionLevelBrief
) {
557 m_callback_baton_sp
->GetDescription(s
->AsRawOstream(), level
,
558 s
->GetIndentLevel());
561 if (!m_condition_text
.empty()) {
562 if (level
!= eDescriptionLevelBrief
) {
564 s
->Printf("Condition: %s\n", m_condition_text
.c_str());
569 void BreakpointOptions::CommandBaton::GetDescription(
570 llvm::raw_ostream
&s
, lldb::DescriptionLevel level
,
571 unsigned indentation
) const {
572 const CommandData
*data
= getItem();
574 if (level
== eDescriptionLevelBrief
) {
576 << ((data
&& data
->user_source
.GetSize() > 0) ? "yes" : "no");
581 s
.indent(indentation
);
582 s
<< "Breakpoint commands";
583 if (data
->interpreter
!= eScriptLanguageNone
)
584 s
<< llvm::formatv(" ({0}):\n",
585 ScriptInterpreter::LanguageToString(data
->interpreter
));
590 if (data
&& data
->user_source
.GetSize() > 0) {
591 for (llvm::StringRef str
: data
->user_source
) {
592 s
.indent(indentation
);
596 s
<< "No commands.\n";
599 void BreakpointOptions::SetCommandDataCallback(
600 std::unique_ptr
<CommandData
> &cmd_data
) {
601 cmd_data
->interpreter
= eScriptLanguageNone
;
602 auto baton_sp
= std::make_shared
<CommandBaton
>(std::move(cmd_data
));
603 SetCallback(BreakpointOptions::BreakpointOptionsCallbackFunction
, baton_sp
);
604 m_set_flags
.Set(eCallback
);
607 bool BreakpointOptions::BreakpointOptionsCallbackFunction(
608 void *baton
, StoppointCallbackContext
*context
, lldb::user_id_t break_id
,
609 lldb::user_id_t break_loc_id
) {
610 bool ret_value
= true;
611 if (baton
== nullptr)
614 CommandData
*data
= (CommandData
*)baton
;
615 StringList
&commands
= data
->user_source
;
617 if (commands
.GetSize() > 0) {
618 ExecutionContext
exe_ctx(context
->exe_ctx_ref
);
619 Target
*target
= exe_ctx
.GetTargetPtr();
621 Debugger
&debugger
= target
->GetDebugger();
622 CommandReturnObject
result(debugger
.GetUseColor());
624 // Rig up the results secondary output stream to the debugger's, so the
625 // output will come out synchronously if the debugger is set up that way.
626 StreamSP
output_stream(debugger
.GetAsyncOutputStream());
627 StreamSP
error_stream(debugger
.GetAsyncErrorStream());
628 result
.SetImmediateOutputStream(output_stream
);
629 result
.SetImmediateErrorStream(error_stream
);
631 CommandInterpreterRunOptions options
;
632 options
.SetStopOnContinue(true);
633 options
.SetStopOnError(data
->stop_on_error
);
634 options
.SetEchoCommands(true);
635 options
.SetPrintResults(true);
636 options
.SetPrintErrors(true);
637 options
.SetAddToHistory(false);
639 debugger
.GetCommandInterpreter().HandleCommands(commands
, exe_ctx
,
641 result
.GetImmediateOutputStream()->Flush();
642 result
.GetImmediateErrorStream()->Flush();
648 void BreakpointOptions::Clear()
651 m_thread_spec_up
.release();
654 m_auto_continue
= false;
655 m_callback
= nullptr;
656 m_callback_baton_sp
.reset();
657 m_baton_is_command_baton
= false;
658 m_callback_is_synchronous
= false;
660 m_condition_text
.clear();