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 llvm::StringRef elem_string
;
92 success
= user_source
->GetItemAtIndexAsString(i
, elem_string
);
94 data_up
->user_source
.AppendString(elem_string
);
101 const char *BreakpointOptions::g_option_names
[(
102 size_t)BreakpointOptions::OptionNames::LastOptionName
]{
103 "ConditionText", "IgnoreCount",
104 "EnabledState", "OneShotState", "AutoContinue"};
106 bool BreakpointOptions::NullCallback(void *baton
,
107 StoppointCallbackContext
*context
,
108 lldb::user_id_t break_id
,
109 lldb::user_id_t break_loc_id
) {
113 // BreakpointOptions constructor
114 BreakpointOptions::BreakpointOptions(bool all_flags_set
)
115 : m_callback(BreakpointOptions::NullCallback
),
116 m_baton_is_command_baton(false), m_callback_is_synchronous(false),
117 m_enabled(true), m_one_shot(false), m_ignore_count(0),
118 m_condition_text_hash(0), m_inject_condition(false),
119 m_auto_continue(false), m_set_flags(0) {
121 m_set_flags
.Set(~((Flags::ValueType
)0));
124 BreakpointOptions::BreakpointOptions(const char *condition
, bool enabled
,
125 int32_t ignore
, bool one_shot
,
127 : m_callback(nullptr), m_baton_is_command_baton(false),
128 m_callback_is_synchronous(false), m_enabled(enabled
),
129 m_one_shot(one_shot
), m_ignore_count(ignore
), m_condition_text_hash(0),
130 m_inject_condition(false), m_auto_continue(auto_continue
) {
131 m_set_flags
.Set(eEnabled
| eIgnoreCount
| eOneShot
| eAutoContinue
);
132 if (condition
&& *condition
!= '\0') {
133 SetCondition(condition
);
137 // BreakpointOptions copy constructor
138 BreakpointOptions::BreakpointOptions(const BreakpointOptions
&rhs
)
139 : m_callback(rhs
.m_callback
), m_callback_baton_sp(rhs
.m_callback_baton_sp
),
140 m_baton_is_command_baton(rhs
.m_baton_is_command_baton
),
141 m_callback_is_synchronous(rhs
.m_callback_is_synchronous
),
142 m_enabled(rhs
.m_enabled
), m_one_shot(rhs
.m_one_shot
),
143 m_ignore_count(rhs
.m_ignore_count
), m_inject_condition(false),
144 m_auto_continue(rhs
.m_auto_continue
), m_set_flags(rhs
.m_set_flags
) {
145 if (rhs
.m_thread_spec_up
!= nullptr)
146 m_thread_spec_up
= std::make_unique
<ThreadSpec
>(*rhs
.m_thread_spec_up
);
147 m_condition_text
= rhs
.m_condition_text
;
148 m_condition_text_hash
= rhs
.m_condition_text_hash
;
151 // BreakpointOptions assignment operator
152 const BreakpointOptions
&BreakpointOptions::
153 operator=(const BreakpointOptions
&rhs
) {
154 m_callback
= rhs
.m_callback
;
155 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
;
159 m_one_shot
= rhs
.m_one_shot
;
160 m_ignore_count
= rhs
.m_ignore_count
;
161 if (rhs
.m_thread_spec_up
!= nullptr)
162 m_thread_spec_up
= std::make_unique
<ThreadSpec
>(*rhs
.m_thread_spec_up
);
163 m_condition_text
= rhs
.m_condition_text
;
164 m_condition_text_hash
= rhs
.m_condition_text_hash
;
165 m_inject_condition
= rhs
.m_inject_condition
;
166 m_auto_continue
= rhs
.m_auto_continue
;
167 m_set_flags
= rhs
.m_set_flags
;
171 void BreakpointOptions::CopyOverSetOptions(const BreakpointOptions
&incoming
)
173 if (incoming
.m_set_flags
.Test(eEnabled
))
175 m_enabled
= incoming
.m_enabled
;
176 m_set_flags
.Set(eEnabled
);
178 if (incoming
.m_set_flags
.Test(eOneShot
))
180 m_one_shot
= incoming
.m_one_shot
;
181 m_set_flags
.Set(eOneShot
);
183 if (incoming
.m_set_flags
.Test(eCallback
))
185 m_callback
= incoming
.m_callback
;
186 m_callback_baton_sp
= incoming
.m_callback_baton_sp
;
187 m_callback_is_synchronous
= incoming
.m_callback_is_synchronous
;
188 m_baton_is_command_baton
= incoming
.m_baton_is_command_baton
;
189 m_set_flags
.Set(eCallback
);
191 if (incoming
.m_set_flags
.Test(eIgnoreCount
))
193 m_ignore_count
= incoming
.m_ignore_count
;
194 m_set_flags
.Set(eIgnoreCount
);
196 if (incoming
.m_set_flags
.Test(eCondition
))
198 // If we're copying over an empty condition, mark it as unset.
199 if (incoming
.m_condition_text
.empty()) {
200 m_condition_text
.clear();
201 m_condition_text_hash
= 0;
202 m_set_flags
.Clear(eCondition
);
204 m_condition_text
= incoming
.m_condition_text
;
205 m_condition_text_hash
= incoming
.m_condition_text_hash
;
206 m_set_flags
.Set(eCondition
);
209 if (incoming
.m_set_flags
.Test(eAutoContinue
))
211 m_auto_continue
= incoming
.m_auto_continue
;
212 m_set_flags
.Set(eAutoContinue
);
214 if (incoming
.m_set_flags
.Test(eThreadSpec
) && incoming
.m_thread_spec_up
) {
215 if (!m_thread_spec_up
)
217 std::make_unique
<ThreadSpec
>(*incoming
.m_thread_spec_up
);
219 *m_thread_spec_up
= *incoming
.m_thread_spec_up
;
220 m_set_flags
.Set(eThreadSpec
);
225 BreakpointOptions::~BreakpointOptions() = default;
227 std::unique_ptr
<BreakpointOptions
> BreakpointOptions::CreateFromStructuredData(
228 Target
&target
, const StructuredData::Dictionary
&options_dict
,
231 bool one_shot
= false;
232 bool auto_continue
= false;
233 uint32_t ignore_count
= 0;
234 llvm::StringRef
condition_ref("");
237 const char *key
= GetKey(OptionNames::EnabledState
);
239 if (key
&& options_dict
.HasKey(key
)) {
240 success
= options_dict
.GetValueForKeyAsBoolean(key
, enabled
);
242 error
.SetErrorStringWithFormat("%s key is not a boolean.", key
);
245 set_options
.Set(eEnabled
);
248 key
= GetKey(OptionNames::OneShotState
);
249 if (key
&& options_dict
.HasKey(key
)) {
250 success
= options_dict
.GetValueForKeyAsBoolean(key
, one_shot
);
252 error
.SetErrorStringWithFormat("%s key is not a boolean.", key
);
255 set_options
.Set(eOneShot
);
258 key
= GetKey(OptionNames::AutoContinue
);
259 if (key
&& options_dict
.HasKey(key
)) {
260 success
= options_dict
.GetValueForKeyAsBoolean(key
, auto_continue
);
262 error
.SetErrorStringWithFormat("%s key is not a boolean.", key
);
265 set_options
.Set(eAutoContinue
);
268 key
= GetKey(OptionNames::IgnoreCount
);
269 if (key
&& options_dict
.HasKey(key
)) {
270 success
= options_dict
.GetValueForKeyAsInteger(key
, ignore_count
);
272 error
.SetErrorStringWithFormat("%s key is not an integer.", key
);
275 set_options
.Set(eIgnoreCount
);
278 key
= GetKey(OptionNames::ConditionText
);
279 if (key
&& options_dict
.HasKey(key
)) {
280 success
= options_dict
.GetValueForKeyAsString(key
, condition_ref
);
282 error
.SetErrorStringWithFormat("%s key is not an string.", key
);
285 set_options
.Set(eCondition
);
288 std::unique_ptr
<CommandData
> cmd_data_up
;
289 StructuredData::Dictionary
*cmds_dict
;
290 success
= options_dict
.GetValueForKeyAsDictionary(
291 CommandData::GetSerializationKey(), cmds_dict
);
292 if (success
&& cmds_dict
) {
294 cmd_data_up
= CommandData::CreateFromStructuredData(*cmds_dict
, cmds_error
);
295 if (cmds_error
.Fail()) {
296 error
.SetErrorStringWithFormat(
297 "Failed to deserialize breakpoint command options: %s.",
298 cmds_error
.AsCString());
303 auto bp_options
= std::make_unique
<BreakpointOptions
>(
304 condition_ref
.str().c_str(), enabled
,
305 ignore_count
, one_shot
, auto_continue
);
307 if (cmd_data_up
->interpreter
== eScriptLanguageNone
)
308 bp_options
->SetCommandDataCallback(cmd_data_up
);
310 ScriptInterpreter
*interp
= target
.GetDebugger().GetScriptInterpreter();
312 error
.SetErrorString(
313 "Can't set script commands - no script interpreter");
316 if (interp
->GetLanguage() != cmd_data_up
->interpreter
) {
317 error
.SetErrorStringWithFormat(
318 "Current script language doesn't match breakpoint's language: %s",
319 ScriptInterpreter::LanguageToString(cmd_data_up
->interpreter
)
325 interp
->SetBreakpointCommandCallback(*bp_options
, cmd_data_up
);
326 if (script_error
.Fail()) {
327 error
.SetErrorStringWithFormat("Error generating script callback: %s.",
334 StructuredData::Dictionary
*thread_spec_dict
;
335 success
= options_dict
.GetValueForKeyAsDictionary(
336 ThreadSpec::GetSerializationKey(), thread_spec_dict
);
338 Status thread_spec_error
;
339 std::unique_ptr
<ThreadSpec
> thread_spec_up
=
340 ThreadSpec::CreateFromStructuredData(*thread_spec_dict
,
342 if (thread_spec_error
.Fail()) {
343 error
.SetErrorStringWithFormat(
344 "Failed to deserialize breakpoint thread spec options: %s.",
345 thread_spec_error
.AsCString());
348 bp_options
->SetThreadSpec(thread_spec_up
);
353 StructuredData::ObjectSP
BreakpointOptions::SerializeToStructuredData() {
354 StructuredData::DictionarySP
options_dict_sp(
355 new StructuredData::Dictionary());
356 if (m_set_flags
.Test(eEnabled
))
357 options_dict_sp
->AddBooleanItem(GetKey(OptionNames::EnabledState
),
359 if (m_set_flags
.Test(eOneShot
))
360 options_dict_sp
->AddBooleanItem(GetKey(OptionNames::OneShotState
),
362 if (m_set_flags
.Test(eAutoContinue
))
363 options_dict_sp
->AddBooleanItem(GetKey(OptionNames::AutoContinue
),
365 if (m_set_flags
.Test(eIgnoreCount
))
366 options_dict_sp
->AddIntegerItem(GetKey(OptionNames::IgnoreCount
),
368 if (m_set_flags
.Test(eCondition
))
369 options_dict_sp
->AddStringItem(GetKey(OptionNames::ConditionText
),
372 if (m_set_flags
.Test(eCallback
) && m_baton_is_command_baton
) {
374 std::static_pointer_cast
<CommandBaton
>(m_callback_baton_sp
);
375 StructuredData::ObjectSP commands_sp
=
376 cmd_baton
->getItem()->SerializeToStructuredData();
378 options_dict_sp
->AddItem(
379 BreakpointOptions::CommandData::GetSerializationKey(), commands_sp
);
382 if (m_set_flags
.Test(eThreadSpec
) && m_thread_spec_up
) {
383 StructuredData::ObjectSP thread_spec_sp
=
384 m_thread_spec_up
->SerializeToStructuredData();
385 options_dict_sp
->AddItem(ThreadSpec::GetSerializationKey(), thread_spec_sp
);
388 return options_dict_sp
;
392 void BreakpointOptions::SetCallback(BreakpointHitCallback callback
,
393 const lldb::BatonSP
&callback_baton_sp
,
394 bool callback_is_synchronous
) {
395 // FIXME: This seems unsafe. If BatonSP actually *is* a CommandBaton, but
396 // in a shared_ptr<Baton> instead of a shared_ptr<CommandBaton>, then we will
397 // set m_baton_is_command_baton to false, which is incorrect. One possible
398 // solution is to make the base Baton class provide a method such as:
399 // virtual StringRef getBatonId() const { return ""; }
400 // and have CommandBaton override this to return something unique, and then
401 // check for it here. Another option might be to make Baton using the llvm
402 // casting infrastructure, so that we could write something like:
403 // if (llvm::isa<CommandBaton>(callback_baton_sp))
404 // at relevant callsites instead of storing a boolean.
405 m_callback_is_synchronous
= callback_is_synchronous
;
406 m_callback
= callback
;
407 m_callback_baton_sp
= callback_baton_sp
;
408 m_baton_is_command_baton
= false;
409 m_set_flags
.Set(eCallback
);
412 void BreakpointOptions::SetCallback(
413 BreakpointHitCallback callback
,
414 const BreakpointOptions::CommandBatonSP
&callback_baton_sp
,
415 bool callback_is_synchronous
) {
416 m_callback_is_synchronous
= callback_is_synchronous
;
417 m_callback
= callback
;
418 m_callback_baton_sp
= callback_baton_sp
;
419 m_baton_is_command_baton
= true;
420 m_set_flags
.Set(eCallback
);
423 void BreakpointOptions::ClearCallback() {
424 m_callback
= BreakpointOptions::NullCallback
;
425 m_callback_is_synchronous
= false;
426 m_callback_baton_sp
.reset();
427 m_baton_is_command_baton
= false;
428 m_set_flags
.Clear(eCallback
);
431 Baton
*BreakpointOptions::GetBaton() { return m_callback_baton_sp
.get(); }
433 const Baton
*BreakpointOptions::GetBaton() const {
434 return m_callback_baton_sp
.get();
437 bool BreakpointOptions::InvokeCallback(StoppointCallbackContext
*context
,
438 lldb::user_id_t break_id
,
439 lldb::user_id_t break_loc_id
) {
441 if (context
->is_synchronous
== IsCallbackSynchronous()) {
442 return m_callback(m_callback_baton_sp
? m_callback_baton_sp
->data()
444 context
, break_id
, break_loc_id
);
445 } else if (IsCallbackSynchronous()) {
452 bool BreakpointOptions::HasCallback() const {
453 return m_callback
!= BreakpointOptions::NullCallback
;
456 bool BreakpointOptions::GetCommandLineCallbacks(StringList
&command_list
) {
459 if (!m_baton_is_command_baton
)
462 auto cmd_baton
= std::static_pointer_cast
<CommandBaton
>(m_callback_baton_sp
);
463 CommandData
*data
= cmd_baton
->getItem();
466 command_list
= data
->user_source
;
470 void BreakpointOptions::SetCondition(const char *condition
) {
471 if (!condition
|| condition
[0] == '\0') {
473 m_set_flags
.Clear(eCondition
);
476 m_set_flags
.Set(eCondition
);
478 m_condition_text
.assign(condition
);
479 std::hash
<std::string
> hasher
;
480 m_condition_text_hash
= hasher(m_condition_text
);
483 const char *BreakpointOptions::GetConditionText(size_t *hash
) const {
484 if (!m_condition_text
.empty()) {
486 *hash
= m_condition_text_hash
;
488 return m_condition_text
.c_str();
494 const ThreadSpec
*BreakpointOptions::GetThreadSpecNoCreate() const {
495 return m_thread_spec_up
.get();
498 ThreadSpec
*BreakpointOptions::GetThreadSpec() {
499 if (m_thread_spec_up
== nullptr) {
500 m_set_flags
.Set(eThreadSpec
);
501 m_thread_spec_up
= std::make_unique
<ThreadSpec
>();
504 return m_thread_spec_up
.get();
507 void BreakpointOptions::SetThreadID(lldb::tid_t thread_id
) {
508 GetThreadSpec()->SetTID(thread_id
);
509 m_set_flags
.Set(eThreadSpec
);
512 void BreakpointOptions::SetThreadSpec(
513 std::unique_ptr
<ThreadSpec
> &thread_spec_up
) {
514 m_thread_spec_up
= std::move(thread_spec_up
);
515 m_set_flags
.Set(eThreadSpec
);
518 void BreakpointOptions::GetDescription(Stream
*s
,
519 lldb::DescriptionLevel level
) const {
520 // Figure out if there are any options not at their default value, and only
521 // print anything if there are:
523 if (m_ignore_count
!= 0 || !m_enabled
|| m_one_shot
|| m_auto_continue
||
524 (GetThreadSpecNoCreate() != nullptr &&
525 GetThreadSpecNoCreate()->HasSpecification())) {
526 if (level
== lldb::eDescriptionLevelVerbose
) {
530 s
->PutCString("Breakpoint Options:\n");
534 s
->PutCString(" Options: ");
536 if (m_ignore_count
> 0)
537 s
->Printf("ignore: %d ", m_ignore_count
);
538 s
->Printf("%sabled ", m_enabled
? "en" : "dis");
541 s
->Printf("one-shot ");
544 s
->Printf("auto-continue ");
546 if (m_thread_spec_up
)
547 m_thread_spec_up
->GetDescription(s
, level
);
549 if (level
== lldb::eDescriptionLevelFull
) {
555 if (m_callback_baton_sp
.get()) {
556 if (level
!= eDescriptionLevelBrief
) {
558 m_callback_baton_sp
->GetDescription(s
->AsRawOstream(), level
,
559 s
->GetIndentLevel());
562 if (!m_condition_text
.empty()) {
563 if (level
!= eDescriptionLevelBrief
) {
565 s
->Printf("Condition: %s\n", m_condition_text
.c_str());
570 void BreakpointOptions::CommandBaton::GetDescription(
571 llvm::raw_ostream
&s
, lldb::DescriptionLevel level
,
572 unsigned indentation
) const {
573 const CommandData
*data
= getItem();
575 if (level
== eDescriptionLevelBrief
) {
577 << ((data
&& data
->user_source
.GetSize() > 0) ? "yes" : "no");
582 s
.indent(indentation
);
583 s
<< "Breakpoint commands";
584 if (data
->interpreter
!= eScriptLanguageNone
)
585 s
<< llvm::formatv(" ({0}):\n",
586 ScriptInterpreter::LanguageToString(data
->interpreter
));
591 if (data
&& data
->user_source
.GetSize() > 0) {
592 for (llvm::StringRef str
: data
->user_source
) {
593 s
.indent(indentation
);
597 s
<< "No commands.\n";
600 void BreakpointOptions::SetCommandDataCallback(
601 std::unique_ptr
<CommandData
> &cmd_data
) {
602 cmd_data
->interpreter
= eScriptLanguageNone
;
603 auto baton_sp
= std::make_shared
<CommandBaton
>(std::move(cmd_data
));
604 SetCallback(BreakpointOptions::BreakpointOptionsCallbackFunction
, baton_sp
);
605 m_set_flags
.Set(eCallback
);
608 bool BreakpointOptions::BreakpointOptionsCallbackFunction(
609 void *baton
, StoppointCallbackContext
*context
, lldb::user_id_t break_id
,
610 lldb::user_id_t break_loc_id
) {
611 bool ret_value
= true;
612 if (baton
== nullptr)
615 CommandData
*data
= (CommandData
*)baton
;
616 StringList
&commands
= data
->user_source
;
618 if (commands
.GetSize() > 0) {
619 ExecutionContext
exe_ctx(context
->exe_ctx_ref
);
620 Target
*target
= exe_ctx
.GetTargetPtr();
622 Debugger
&debugger
= target
->GetDebugger();
623 CommandReturnObject
result(debugger
.GetUseColor());
625 // Rig up the results secondary output stream to the debugger's, so the
626 // output will come out synchronously if the debugger is set up that way.
627 StreamSP
output_stream(debugger
.GetAsyncOutputStream());
628 StreamSP
error_stream(debugger
.GetAsyncErrorStream());
629 result
.SetImmediateOutputStream(output_stream
);
630 result
.SetImmediateErrorStream(error_stream
);
632 CommandInterpreterRunOptions options
;
633 options
.SetStopOnContinue(true);
634 options
.SetStopOnError(data
->stop_on_error
);
635 options
.SetEchoCommands(true);
636 options
.SetPrintResults(true);
637 options
.SetPrintErrors(true);
638 options
.SetAddToHistory(false);
640 debugger
.GetCommandInterpreter().HandleCommands(commands
, exe_ctx
,
642 result
.GetImmediateOutputStream()->Flush();
643 result
.GetImmediateErrorStream()->Flush();
649 void BreakpointOptions::Clear()
652 m_thread_spec_up
.release();
655 m_auto_continue
= false;
656 m_callback
= nullptr;
657 m_callback_baton_sp
.reset();
658 m_baton_is_command_baton
= false;
659 m_callback_is_synchronous
= false;
661 m_condition_text
.clear();