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
= Status::FromErrorString("Missing command language value.");
77 interp_language
= ScriptInterpreter::StringToLanguage(interpreter_str
);
78 if (interp_language
== eScriptLanguageUnknown
) {
79 error
= Status::FromErrorStringWithFormatv(
80 "Unknown breakpoint command language: {0}.", interpreter_str
);
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 // BreakpointOptions constructor
106 BreakpointOptions::BreakpointOptions(bool all_flags_set
)
107 : m_callback(nullptr), m_baton_is_command_baton(false),
108 m_callback_is_synchronous(false), m_enabled(true), m_one_shot(false),
109 m_ignore_count(0), m_condition_text_hash(0), m_inject_condition(false),
110 m_auto_continue(false), m_set_flags(0) {
112 m_set_flags
.Set(~((Flags::ValueType
)0));
115 BreakpointOptions::BreakpointOptions(const char *condition
, bool enabled
,
116 int32_t ignore
, bool one_shot
,
118 : m_callback(nullptr), m_baton_is_command_baton(false),
119 m_callback_is_synchronous(false), m_enabled(enabled
),
120 m_one_shot(one_shot
), m_ignore_count(ignore
), m_condition_text_hash(0),
121 m_inject_condition(false), m_auto_continue(auto_continue
) {
122 m_set_flags
.Set(eEnabled
| eIgnoreCount
| eOneShot
| eAutoContinue
);
123 if (condition
&& *condition
!= '\0') {
124 SetCondition(condition
);
128 // BreakpointOptions copy constructor
129 BreakpointOptions::BreakpointOptions(const BreakpointOptions
&rhs
)
130 : m_callback(rhs
.m_callback
), m_callback_baton_sp(rhs
.m_callback_baton_sp
),
131 m_baton_is_command_baton(rhs
.m_baton_is_command_baton
),
132 m_callback_is_synchronous(rhs
.m_callback_is_synchronous
),
133 m_enabled(rhs
.m_enabled
), m_one_shot(rhs
.m_one_shot
),
134 m_ignore_count(rhs
.m_ignore_count
), m_inject_condition(false),
135 m_auto_continue(rhs
.m_auto_continue
), m_set_flags(rhs
.m_set_flags
) {
136 if (rhs
.m_thread_spec_up
!= nullptr)
137 m_thread_spec_up
= std::make_unique
<ThreadSpec
>(*rhs
.m_thread_spec_up
);
138 m_condition_text
= rhs
.m_condition_text
;
139 m_condition_text_hash
= rhs
.m_condition_text_hash
;
142 // BreakpointOptions assignment operator
143 const BreakpointOptions
&BreakpointOptions::
144 operator=(const BreakpointOptions
&rhs
) {
145 m_callback
= rhs
.m_callback
;
146 m_callback_baton_sp
= rhs
.m_callback_baton_sp
;
147 m_baton_is_command_baton
= rhs
.m_baton_is_command_baton
;
148 m_callback_is_synchronous
= rhs
.m_callback_is_synchronous
;
149 m_enabled
= rhs
.m_enabled
;
150 m_one_shot
= rhs
.m_one_shot
;
151 m_ignore_count
= rhs
.m_ignore_count
;
152 if (rhs
.m_thread_spec_up
!= nullptr)
153 m_thread_spec_up
= std::make_unique
<ThreadSpec
>(*rhs
.m_thread_spec_up
);
154 m_condition_text
= rhs
.m_condition_text
;
155 m_condition_text_hash
= rhs
.m_condition_text_hash
;
156 m_inject_condition
= rhs
.m_inject_condition
;
157 m_auto_continue
= rhs
.m_auto_continue
;
158 m_set_flags
= rhs
.m_set_flags
;
162 void BreakpointOptions::CopyOverSetOptions(const BreakpointOptions
&incoming
)
164 if (incoming
.m_set_flags
.Test(eEnabled
))
166 m_enabled
= incoming
.m_enabled
;
167 m_set_flags
.Set(eEnabled
);
169 if (incoming
.m_set_flags
.Test(eOneShot
))
171 m_one_shot
= incoming
.m_one_shot
;
172 m_set_flags
.Set(eOneShot
);
174 if (incoming
.m_set_flags
.Test(eCallback
))
176 m_callback
= incoming
.m_callback
;
177 m_callback_baton_sp
= incoming
.m_callback_baton_sp
;
178 m_callback_is_synchronous
= incoming
.m_callback_is_synchronous
;
179 m_baton_is_command_baton
= incoming
.m_baton_is_command_baton
;
180 m_set_flags
.Set(eCallback
);
182 if (incoming
.m_set_flags
.Test(eIgnoreCount
))
184 m_ignore_count
= incoming
.m_ignore_count
;
185 m_set_flags
.Set(eIgnoreCount
);
187 if (incoming
.m_set_flags
.Test(eCondition
))
189 // If we're copying over an empty condition, mark it as unset.
190 if (incoming
.m_condition_text
.empty()) {
191 m_condition_text
.clear();
192 m_condition_text_hash
= 0;
193 m_set_flags
.Clear(eCondition
);
195 m_condition_text
= incoming
.m_condition_text
;
196 m_condition_text_hash
= incoming
.m_condition_text_hash
;
197 m_set_flags
.Set(eCondition
);
200 if (incoming
.m_set_flags
.Test(eAutoContinue
))
202 m_auto_continue
= incoming
.m_auto_continue
;
203 m_set_flags
.Set(eAutoContinue
);
205 if (incoming
.m_set_flags
.Test(eThreadSpec
) && incoming
.m_thread_spec_up
) {
206 if (!m_thread_spec_up
)
208 std::make_unique
<ThreadSpec
>(*incoming
.m_thread_spec_up
);
210 *m_thread_spec_up
= *incoming
.m_thread_spec_up
;
211 m_set_flags
.Set(eThreadSpec
);
216 BreakpointOptions::~BreakpointOptions() = default;
218 std::unique_ptr
<BreakpointOptions
> BreakpointOptions::CreateFromStructuredData(
219 Target
&target
, const StructuredData::Dictionary
&options_dict
,
222 bool one_shot
= false;
223 bool auto_continue
= false;
224 uint32_t ignore_count
= 0;
225 llvm::StringRef
condition_ref("");
228 const char *key
= GetKey(OptionNames::EnabledState
);
230 if (key
&& options_dict
.HasKey(key
)) {
231 success
= options_dict
.GetValueForKeyAsBoolean(key
, enabled
);
234 Status::FromErrorStringWithFormat("%s key is not a boolean.", key
);
237 set_options
.Set(eEnabled
);
240 key
= GetKey(OptionNames::OneShotState
);
241 if (key
&& options_dict
.HasKey(key
)) {
242 success
= options_dict
.GetValueForKeyAsBoolean(key
, one_shot
);
245 Status::FromErrorStringWithFormat("%s key is not a boolean.", key
);
248 set_options
.Set(eOneShot
);
251 key
= GetKey(OptionNames::AutoContinue
);
252 if (key
&& options_dict
.HasKey(key
)) {
253 success
= options_dict
.GetValueForKeyAsBoolean(key
, auto_continue
);
256 Status::FromErrorStringWithFormat("%s key is not a boolean.", key
);
259 set_options
.Set(eAutoContinue
);
262 key
= GetKey(OptionNames::IgnoreCount
);
263 if (key
&& options_dict
.HasKey(key
)) {
264 success
= options_dict
.GetValueForKeyAsInteger(key
, ignore_count
);
267 Status::FromErrorStringWithFormat("%s key is not an integer.", key
);
270 set_options
.Set(eIgnoreCount
);
273 key
= GetKey(OptionNames::ConditionText
);
274 if (key
&& options_dict
.HasKey(key
)) {
275 success
= options_dict
.GetValueForKeyAsString(key
, condition_ref
);
278 Status::FromErrorStringWithFormat("%s key is not an string.", key
);
281 set_options
.Set(eCondition
);
284 std::unique_ptr
<CommandData
> cmd_data_up
;
285 StructuredData::Dictionary
*cmds_dict
;
286 success
= options_dict
.GetValueForKeyAsDictionary(
287 CommandData::GetSerializationKey(), cmds_dict
);
288 if (success
&& cmds_dict
) {
290 cmd_data_up
= CommandData::CreateFromStructuredData(*cmds_dict
, cmds_error
);
291 if (cmds_error
.Fail()) {
292 error
= Status::FromErrorStringWithFormat(
293 "Failed to deserialize breakpoint command options: %s.",
294 cmds_error
.AsCString());
299 auto bp_options
= std::make_unique
<BreakpointOptions
>(
300 condition_ref
.str().c_str(), enabled
,
301 ignore_count
, one_shot
, auto_continue
);
303 if (cmd_data_up
->interpreter
== eScriptLanguageNone
)
304 bp_options
->SetCommandDataCallback(cmd_data_up
);
306 ScriptInterpreter
*interp
= target
.GetDebugger().GetScriptInterpreter();
308 error
= Status::FromErrorString(
309 "Can't set script commands - no script interpreter");
312 if (interp
->GetLanguage() != cmd_data_up
->interpreter
) {
313 error
= Status::FromErrorStringWithFormat(
314 "Current script language doesn't match breakpoint's language: %s",
315 ScriptInterpreter::LanguageToString(cmd_data_up
->interpreter
)
321 interp
->SetBreakpointCommandCallback(*bp_options
, cmd_data_up
);
322 if (script_error
.Fail()) {
323 error
= Status::FromErrorStringWithFormat(
324 "Error generating script callback: %s.", error
.AsCString());
330 StructuredData::Dictionary
*thread_spec_dict
;
331 success
= options_dict
.GetValueForKeyAsDictionary(
332 ThreadSpec::GetSerializationKey(), thread_spec_dict
);
334 Status thread_spec_error
;
335 std::unique_ptr
<ThreadSpec
> thread_spec_up
=
336 ThreadSpec::CreateFromStructuredData(*thread_spec_dict
,
338 if (thread_spec_error
.Fail()) {
339 error
= Status::FromErrorStringWithFormat(
340 "Failed to deserialize breakpoint thread spec options: %s.",
341 thread_spec_error
.AsCString());
344 bp_options
->SetThreadSpec(thread_spec_up
);
349 StructuredData::ObjectSP
BreakpointOptions::SerializeToStructuredData() {
350 StructuredData::DictionarySP
options_dict_sp(
351 new StructuredData::Dictionary());
352 if (m_set_flags
.Test(eEnabled
))
353 options_dict_sp
->AddBooleanItem(GetKey(OptionNames::EnabledState
),
355 if (m_set_flags
.Test(eOneShot
))
356 options_dict_sp
->AddBooleanItem(GetKey(OptionNames::OneShotState
),
358 if (m_set_flags
.Test(eAutoContinue
))
359 options_dict_sp
->AddBooleanItem(GetKey(OptionNames::AutoContinue
),
361 if (m_set_flags
.Test(eIgnoreCount
))
362 options_dict_sp
->AddIntegerItem(GetKey(OptionNames::IgnoreCount
),
364 if (m_set_flags
.Test(eCondition
))
365 options_dict_sp
->AddStringItem(GetKey(OptionNames::ConditionText
),
368 if (m_set_flags
.Test(eCallback
) && m_baton_is_command_baton
) {
370 std::static_pointer_cast
<CommandBaton
>(m_callback_baton_sp
);
371 StructuredData::ObjectSP commands_sp
=
372 cmd_baton
->getItem()->SerializeToStructuredData();
374 options_dict_sp
->AddItem(
375 BreakpointOptions::CommandData::GetSerializationKey(), commands_sp
);
378 if (m_set_flags
.Test(eThreadSpec
) && m_thread_spec_up
) {
379 StructuredData::ObjectSP thread_spec_sp
=
380 m_thread_spec_up
->SerializeToStructuredData();
381 options_dict_sp
->AddItem(ThreadSpec::GetSerializationKey(), thread_spec_sp
);
384 return options_dict_sp
;
388 void BreakpointOptions::SetCallback(BreakpointHitCallback callback
,
389 const lldb::BatonSP
&callback_baton_sp
,
390 bool callback_is_synchronous
) {
391 // FIXME: This seems unsafe. If BatonSP actually *is* a CommandBaton, but
392 // in a shared_ptr<Baton> instead of a shared_ptr<CommandBaton>, then we will
393 // set m_baton_is_command_baton to false, which is incorrect. One possible
394 // solution is to make the base Baton class provide a method such as:
395 // virtual StringRef getBatonId() const { return ""; }
396 // and have CommandBaton override this to return something unique, and then
397 // check for it here. Another option might be to make Baton using the llvm
398 // casting infrastructure, so that we could write something like:
399 // if (llvm::isa<CommandBaton>(callback_baton_sp))
400 // at relevant callsites instead of storing a boolean.
401 m_callback_is_synchronous
= callback_is_synchronous
;
402 m_callback
= callback
;
403 m_callback_baton_sp
= callback_baton_sp
;
404 m_baton_is_command_baton
= false;
405 m_set_flags
.Set(eCallback
);
408 void BreakpointOptions::SetCallback(
409 BreakpointHitCallback callback
,
410 const BreakpointOptions::CommandBatonSP
&callback_baton_sp
,
411 bool callback_is_synchronous
) {
412 m_callback_is_synchronous
= callback_is_synchronous
;
413 m_callback
= callback
;
414 m_callback_baton_sp
= callback_baton_sp
;
415 m_baton_is_command_baton
= true;
416 m_set_flags
.Set(eCallback
);
419 void BreakpointOptions::ClearCallback() {
420 m_callback
= nullptr;
421 m_callback_is_synchronous
= false;
422 m_callback_baton_sp
.reset();
423 m_baton_is_command_baton
= false;
424 m_set_flags
.Clear(eCallback
);
427 Baton
*BreakpointOptions::GetBaton() { return m_callback_baton_sp
.get(); }
429 const Baton
*BreakpointOptions::GetBaton() const {
430 return m_callback_baton_sp
.get();
433 bool BreakpointOptions::InvokeCallback(StoppointCallbackContext
*context
,
434 lldb::user_id_t break_id
,
435 lldb::user_id_t break_loc_id
) {
437 if (context
->is_synchronous
== IsCallbackSynchronous()) {
438 return m_callback(m_callback_baton_sp
? m_callback_baton_sp
->data()
440 context
, break_id
, break_loc_id
);
441 } else if (IsCallbackSynchronous()) {
448 bool BreakpointOptions::HasCallback() const {
449 return static_cast<bool>(m_callback
);
452 bool BreakpointOptions::GetCommandLineCallbacks(StringList
&command_list
) {
455 if (!m_baton_is_command_baton
)
458 auto cmd_baton
= std::static_pointer_cast
<CommandBaton
>(m_callback_baton_sp
);
459 CommandData
*data
= cmd_baton
->getItem();
462 command_list
= data
->user_source
;
466 void BreakpointOptions::SetCondition(const char *condition
) {
467 if (!condition
|| condition
[0] == '\0') {
469 m_set_flags
.Clear(eCondition
);
472 m_set_flags
.Set(eCondition
);
474 m_condition_text
.assign(condition
);
475 std::hash
<std::string
> hasher
;
476 m_condition_text_hash
= hasher(m_condition_text
);
479 const char *BreakpointOptions::GetConditionText(size_t *hash
) const {
480 if (!m_condition_text
.empty()) {
482 *hash
= m_condition_text_hash
;
484 return m_condition_text
.c_str();
490 const ThreadSpec
*BreakpointOptions::GetThreadSpecNoCreate() const {
491 return m_thread_spec_up
.get();
494 ThreadSpec
*BreakpointOptions::GetThreadSpec() {
495 if (m_thread_spec_up
== nullptr) {
496 m_set_flags
.Set(eThreadSpec
);
497 m_thread_spec_up
= std::make_unique
<ThreadSpec
>();
500 return m_thread_spec_up
.get();
503 void BreakpointOptions::SetThreadID(lldb::tid_t thread_id
) {
504 GetThreadSpec()->SetTID(thread_id
);
505 m_set_flags
.Set(eThreadSpec
);
508 void BreakpointOptions::SetThreadSpec(
509 std::unique_ptr
<ThreadSpec
> &thread_spec_up
) {
510 m_thread_spec_up
= std::move(thread_spec_up
);
511 m_set_flags
.Set(eThreadSpec
);
514 void BreakpointOptions::GetDescription(Stream
*s
,
515 lldb::DescriptionLevel level
) const {
516 // Figure out if there are any options not at their default value, and only
517 // print anything if there are:
519 if (m_ignore_count
!= 0 || !m_enabled
|| m_one_shot
|| m_auto_continue
||
520 (GetThreadSpecNoCreate() != nullptr &&
521 GetThreadSpecNoCreate()->HasSpecification())) {
522 if (level
== lldb::eDescriptionLevelVerbose
) {
526 s
->PutCString("Breakpoint Options:\n");
530 s
->PutCString(" Options: ");
532 if (m_ignore_count
> 0)
533 s
->Printf("ignore: %d ", m_ignore_count
);
534 s
->Printf("%sabled ", m_enabled
? "en" : "dis");
537 s
->Printf("one-shot ");
540 s
->Printf("auto-continue ");
542 if (m_thread_spec_up
)
543 m_thread_spec_up
->GetDescription(s
, level
);
545 if (level
== lldb::eDescriptionLevelFull
) {
551 if (m_callback_baton_sp
.get()) {
552 if (level
!= eDescriptionLevelBrief
) {
554 m_callback_baton_sp
->GetDescription(s
->AsRawOstream(), level
,
555 s
->GetIndentLevel());
558 if (!m_condition_text
.empty()) {
559 if (level
!= eDescriptionLevelBrief
) {
561 s
->Printf("Condition: %s\n", m_condition_text
.c_str());
566 void BreakpointOptions::CommandBaton::GetDescription(
567 llvm::raw_ostream
&s
, lldb::DescriptionLevel level
,
568 unsigned indentation
) const {
569 const CommandData
*data
= getItem();
571 if (level
== eDescriptionLevelBrief
) {
573 << ((data
&& data
->user_source
.GetSize() > 0) ? "yes" : "no");
578 s
.indent(indentation
);
579 s
<< "Breakpoint commands";
580 if (data
->interpreter
!= eScriptLanguageNone
)
581 s
<< llvm::formatv(" ({0}):\n",
582 ScriptInterpreter::LanguageToString(data
->interpreter
));
587 if (data
&& data
->user_source
.GetSize() > 0) {
588 for (llvm::StringRef str
: data
->user_source
) {
589 s
.indent(indentation
);
593 s
<< "No commands.\n";
596 void BreakpointOptions::SetCommandDataCallback(
597 std::unique_ptr
<CommandData
> &cmd_data
) {
598 cmd_data
->interpreter
= eScriptLanguageNone
;
599 auto baton_sp
= std::make_shared
<CommandBaton
>(std::move(cmd_data
));
600 SetCallback(BreakpointOptions::BreakpointOptionsCallbackFunction
, baton_sp
);
601 m_set_flags
.Set(eCallback
);
604 bool BreakpointOptions::BreakpointOptionsCallbackFunction(
605 void *baton
, StoppointCallbackContext
*context
, lldb::user_id_t break_id
,
606 lldb::user_id_t break_loc_id
) {
607 bool ret_value
= true;
608 if (baton
== nullptr)
611 CommandData
*data
= (CommandData
*)baton
;
612 StringList
&commands
= data
->user_source
;
614 if (commands
.GetSize() > 0) {
615 ExecutionContext
exe_ctx(context
->exe_ctx_ref
);
616 Target
*target
= exe_ctx
.GetTargetPtr();
618 Debugger
&debugger
= target
->GetDebugger();
619 CommandReturnObject
result(debugger
.GetUseColor());
621 // Rig up the results secondary output stream to the debugger's, so the
622 // output will come out synchronously if the debugger is set up that way.
623 StreamSP
output_stream(debugger
.GetAsyncOutputStream());
624 StreamSP
error_stream(debugger
.GetAsyncErrorStream());
625 result
.SetImmediateOutputStream(output_stream
);
626 result
.SetImmediateErrorStream(error_stream
);
628 CommandInterpreterRunOptions options
;
629 options
.SetStopOnContinue(true);
630 options
.SetStopOnError(data
->stop_on_error
);
631 options
.SetEchoCommands(true);
632 options
.SetPrintResults(true);
633 options
.SetPrintErrors(true);
634 options
.SetAddToHistory(false);
636 debugger
.GetCommandInterpreter().HandleCommands(commands
, exe_ctx
,
638 result
.GetImmediateOutputStream()->Flush();
639 result
.GetImmediateErrorStream()->Flush();
645 void BreakpointOptions::Clear()
648 m_thread_spec_up
.release();
651 m_auto_continue
= false;
652 m_callback
= nullptr;
653 m_callback_baton_sp
.reset();
654 m_baton_is_command_baton
= false;
655 m_callback_is_synchronous
= false;
657 m_condition_text
.clear();