1dda28197Spatrick //===-- BreakpointOptions.cpp ---------------------------------------------===//
2061da546Spatrick //
3061da546Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4061da546Spatrick // See https://llvm.org/LICENSE.txt for license information.
5061da546Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6061da546Spatrick //
7061da546Spatrick //===----------------------------------------------------------------------===//
8061da546Spatrick
9061da546Spatrick #include "lldb/Breakpoint/BreakpointOptions.h"
10061da546Spatrick
11061da546Spatrick #include "lldb/Breakpoint/StoppointCallbackContext.h"
12061da546Spatrick #include "lldb/Core/Value.h"
13061da546Spatrick #include "lldb/Interpreter/CommandInterpreter.h"
14061da546Spatrick #include "lldb/Interpreter/CommandReturnObject.h"
15061da546Spatrick #include "lldb/Target/Process.h"
16061da546Spatrick #include "lldb/Target/Target.h"
17061da546Spatrick #include "lldb/Target/ThreadSpec.h"
18061da546Spatrick #include "lldb/Utility/Stream.h"
19061da546Spatrick #include "lldb/Utility/StringList.h"
20061da546Spatrick
21061da546Spatrick #include "llvm/ADT/STLExtras.h"
22061da546Spatrick
23061da546Spatrick using namespace lldb;
24061da546Spatrick using namespace lldb_private;
25061da546Spatrick
26061da546Spatrick const char
27061da546Spatrick *BreakpointOptions::CommandData::g_option_names[static_cast<uint32_t>(
28061da546Spatrick BreakpointOptions::CommandData::OptionNames::LastOptionName)]{
29061da546Spatrick "UserSource", "ScriptSource", "StopOnError"};
30061da546Spatrick
31061da546Spatrick StructuredData::ObjectSP
SerializeToStructuredData()32061da546Spatrick BreakpointOptions::CommandData::SerializeToStructuredData() {
33061da546Spatrick size_t num_strings = user_source.GetSize();
34061da546Spatrick if (num_strings == 0 && script_source.empty()) {
35061da546Spatrick // We shouldn't serialize commands if there aren't any, return an empty sp
36061da546Spatrick // to indicate this.
37061da546Spatrick return StructuredData::ObjectSP();
38061da546Spatrick }
39061da546Spatrick
40061da546Spatrick StructuredData::DictionarySP options_dict_sp(
41061da546Spatrick new StructuredData::Dictionary());
42061da546Spatrick options_dict_sp->AddBooleanItem(GetKey(OptionNames::StopOnError),
43061da546Spatrick stop_on_error);
44061da546Spatrick
45061da546Spatrick StructuredData::ArraySP user_source_sp(new StructuredData::Array());
46061da546Spatrick for (size_t i = 0; i < num_strings; i++) {
47061da546Spatrick StructuredData::StringSP item_sp(
48061da546Spatrick new StructuredData::String(user_source[i]));
49061da546Spatrick user_source_sp->AddItem(item_sp);
50061da546Spatrick options_dict_sp->AddItem(GetKey(OptionNames::UserSource), user_source_sp);
51061da546Spatrick }
52061da546Spatrick
53061da546Spatrick options_dict_sp->AddStringItem(
54061da546Spatrick GetKey(OptionNames::Interpreter),
55061da546Spatrick ScriptInterpreter::LanguageToString(interpreter));
56061da546Spatrick return options_dict_sp;
57061da546Spatrick }
58061da546Spatrick
59061da546Spatrick std::unique_ptr<BreakpointOptions::CommandData>
CreateFromStructuredData(const StructuredData::Dictionary & options_dict,Status & error)60061da546Spatrick BreakpointOptions::CommandData::CreateFromStructuredData(
61061da546Spatrick const StructuredData::Dictionary &options_dict, Status &error) {
62061da546Spatrick std::unique_ptr<CommandData> data_up(new CommandData());
63061da546Spatrick
64061da546Spatrick bool success = options_dict.GetValueForKeyAsBoolean(
65061da546Spatrick GetKey(OptionNames::StopOnError), data_up->stop_on_error);
66061da546Spatrick
67061da546Spatrick llvm::StringRef interpreter_str;
68061da546Spatrick ScriptLanguage interp_language;
69061da546Spatrick success = options_dict.GetValueForKeyAsString(
70061da546Spatrick GetKey(OptionNames::Interpreter), interpreter_str);
71061da546Spatrick
72061da546Spatrick if (!success) {
73061da546Spatrick error.SetErrorString("Missing command language value.");
74061da546Spatrick return data_up;
75061da546Spatrick }
76061da546Spatrick
77061da546Spatrick interp_language = ScriptInterpreter::StringToLanguage(interpreter_str);
78061da546Spatrick if (interp_language == eScriptLanguageUnknown) {
79061da546Spatrick error.SetErrorStringWithFormatv("Unknown breakpoint command language: {0}.",
80061da546Spatrick interpreter_str);
81061da546Spatrick return data_up;
82061da546Spatrick }
83061da546Spatrick data_up->interpreter = interp_language;
84061da546Spatrick
85061da546Spatrick StructuredData::Array *user_source;
86061da546Spatrick success = options_dict.GetValueForKeyAsArray(GetKey(OptionNames::UserSource),
87061da546Spatrick user_source);
88061da546Spatrick if (success) {
89061da546Spatrick size_t num_elems = user_source->GetSize();
90061da546Spatrick for (size_t i = 0; i < num_elems; i++) {
91061da546Spatrick llvm::StringRef elem_string;
92061da546Spatrick success = user_source->GetItemAtIndexAsString(i, elem_string);
93061da546Spatrick if (success)
94061da546Spatrick data_up->user_source.AppendString(elem_string);
95061da546Spatrick }
96061da546Spatrick }
97061da546Spatrick
98061da546Spatrick return data_up;
99061da546Spatrick }
100061da546Spatrick
101061da546Spatrick const char *BreakpointOptions::g_option_names[(
102061da546Spatrick size_t)BreakpointOptions::OptionNames::LastOptionName]{
103061da546Spatrick "ConditionText", "IgnoreCount",
104061da546Spatrick "EnabledState", "OneShotState", "AutoContinue"};
105061da546Spatrick
NullCallback(void * baton,StoppointCallbackContext * context,lldb::user_id_t break_id,lldb::user_id_t break_loc_id)106061da546Spatrick bool BreakpointOptions::NullCallback(void *baton,
107061da546Spatrick StoppointCallbackContext *context,
108061da546Spatrick lldb::user_id_t break_id,
109061da546Spatrick lldb::user_id_t break_loc_id) {
110061da546Spatrick return true;
111061da546Spatrick }
112061da546Spatrick
113061da546Spatrick // BreakpointOptions constructor
BreakpointOptions(bool all_flags_set)114061da546Spatrick BreakpointOptions::BreakpointOptions(bool all_flags_set)
115*f6aab3d8Srobert : m_callback(BreakpointOptions::NullCallback),
116061da546Spatrick m_baton_is_command_baton(false), m_callback_is_synchronous(false),
117*f6aab3d8Srobert m_enabled(true), m_one_shot(false), m_ignore_count(0),
118*f6aab3d8Srobert m_condition_text_hash(0), m_inject_condition(false),
119*f6aab3d8Srobert m_auto_continue(false), m_set_flags(0) {
120061da546Spatrick if (all_flags_set)
121061da546Spatrick m_set_flags.Set(~((Flags::ValueType)0));
122061da546Spatrick }
123061da546Spatrick
BreakpointOptions(const char * condition,bool enabled,int32_t ignore,bool one_shot,bool auto_continue)124061da546Spatrick BreakpointOptions::BreakpointOptions(const char *condition, bool enabled,
125061da546Spatrick int32_t ignore, bool one_shot,
126061da546Spatrick bool auto_continue)
127061da546Spatrick : m_callback(nullptr), m_baton_is_command_baton(false),
128061da546Spatrick m_callback_is_synchronous(false), m_enabled(enabled),
129*f6aab3d8Srobert m_one_shot(one_shot), m_ignore_count(ignore), m_condition_text_hash(0),
130*f6aab3d8Srobert m_inject_condition(false), m_auto_continue(auto_continue) {
131*f6aab3d8Srobert m_set_flags.Set(eEnabled | eIgnoreCount | eOneShot | eAutoContinue);
132061da546Spatrick if (condition && *condition != '\0') {
133061da546Spatrick SetCondition(condition);
134061da546Spatrick }
135061da546Spatrick }
136061da546Spatrick
137061da546Spatrick // BreakpointOptions copy constructor
BreakpointOptions(const BreakpointOptions & rhs)138061da546Spatrick BreakpointOptions::BreakpointOptions(const BreakpointOptions &rhs)
139061da546Spatrick : m_callback(rhs.m_callback), m_callback_baton_sp(rhs.m_callback_baton_sp),
140061da546Spatrick m_baton_is_command_baton(rhs.m_baton_is_command_baton),
141061da546Spatrick m_callback_is_synchronous(rhs.m_callback_is_synchronous),
142061da546Spatrick m_enabled(rhs.m_enabled), m_one_shot(rhs.m_one_shot),
143*f6aab3d8Srobert m_ignore_count(rhs.m_ignore_count), m_inject_condition(false),
144061da546Spatrick m_auto_continue(rhs.m_auto_continue), m_set_flags(rhs.m_set_flags) {
145061da546Spatrick if (rhs.m_thread_spec_up != nullptr)
146dda28197Spatrick m_thread_spec_up = std::make_unique<ThreadSpec>(*rhs.m_thread_spec_up);
147061da546Spatrick m_condition_text = rhs.m_condition_text;
148061da546Spatrick m_condition_text_hash = rhs.m_condition_text_hash;
149061da546Spatrick }
150061da546Spatrick
151061da546Spatrick // BreakpointOptions assignment operator
152061da546Spatrick const BreakpointOptions &BreakpointOptions::
operator =(const BreakpointOptions & rhs)153061da546Spatrick operator=(const BreakpointOptions &rhs) {
154061da546Spatrick m_callback = rhs.m_callback;
155061da546Spatrick m_callback_baton_sp = rhs.m_callback_baton_sp;
156061da546Spatrick m_baton_is_command_baton = rhs.m_baton_is_command_baton;
157061da546Spatrick m_callback_is_synchronous = rhs.m_callback_is_synchronous;
158061da546Spatrick m_enabled = rhs.m_enabled;
159061da546Spatrick m_one_shot = rhs.m_one_shot;
160061da546Spatrick m_ignore_count = rhs.m_ignore_count;
161061da546Spatrick if (rhs.m_thread_spec_up != nullptr)
162dda28197Spatrick m_thread_spec_up = std::make_unique<ThreadSpec>(*rhs.m_thread_spec_up);
163061da546Spatrick m_condition_text = rhs.m_condition_text;
164061da546Spatrick m_condition_text_hash = rhs.m_condition_text_hash;
165*f6aab3d8Srobert m_inject_condition = rhs.m_inject_condition;
166061da546Spatrick m_auto_continue = rhs.m_auto_continue;
167061da546Spatrick m_set_flags = rhs.m_set_flags;
168061da546Spatrick return *this;
169061da546Spatrick }
170061da546Spatrick
CopyOverSetOptions(const BreakpointOptions & incoming)171061da546Spatrick void BreakpointOptions::CopyOverSetOptions(const BreakpointOptions &incoming)
172061da546Spatrick {
173061da546Spatrick if (incoming.m_set_flags.Test(eEnabled))
174061da546Spatrick {
175061da546Spatrick m_enabled = incoming.m_enabled;
176061da546Spatrick m_set_flags.Set(eEnabled);
177061da546Spatrick }
178061da546Spatrick if (incoming.m_set_flags.Test(eOneShot))
179061da546Spatrick {
180061da546Spatrick m_one_shot = incoming.m_one_shot;
181061da546Spatrick m_set_flags.Set(eOneShot);
182061da546Spatrick }
183061da546Spatrick if (incoming.m_set_flags.Test(eCallback))
184061da546Spatrick {
185061da546Spatrick m_callback = incoming.m_callback;
186061da546Spatrick m_callback_baton_sp = incoming.m_callback_baton_sp;
187061da546Spatrick m_callback_is_synchronous = incoming.m_callback_is_synchronous;
188061da546Spatrick m_baton_is_command_baton = incoming.m_baton_is_command_baton;
189061da546Spatrick m_set_flags.Set(eCallback);
190061da546Spatrick }
191061da546Spatrick if (incoming.m_set_flags.Test(eIgnoreCount))
192061da546Spatrick {
193061da546Spatrick m_ignore_count = incoming.m_ignore_count;
194061da546Spatrick m_set_flags.Set(eIgnoreCount);
195061da546Spatrick }
196061da546Spatrick if (incoming.m_set_flags.Test(eCondition))
197061da546Spatrick {
198061da546Spatrick // If we're copying over an empty condition, mark it as unset.
199061da546Spatrick if (incoming.m_condition_text.empty()) {
200061da546Spatrick m_condition_text.clear();
201061da546Spatrick m_condition_text_hash = 0;
202061da546Spatrick m_set_flags.Clear(eCondition);
203061da546Spatrick } else {
204061da546Spatrick m_condition_text = incoming.m_condition_text;
205061da546Spatrick m_condition_text_hash = incoming.m_condition_text_hash;
206061da546Spatrick m_set_flags.Set(eCondition);
207061da546Spatrick }
208061da546Spatrick }
209061da546Spatrick if (incoming.m_set_flags.Test(eAutoContinue))
210061da546Spatrick {
211061da546Spatrick m_auto_continue = incoming.m_auto_continue;
212061da546Spatrick m_set_flags.Set(eAutoContinue);
213061da546Spatrick }
214061da546Spatrick if (incoming.m_set_flags.Test(eThreadSpec) && incoming.m_thread_spec_up) {
215061da546Spatrick if (!m_thread_spec_up)
216dda28197Spatrick m_thread_spec_up =
217dda28197Spatrick std::make_unique<ThreadSpec>(*incoming.m_thread_spec_up);
218061da546Spatrick else
219061da546Spatrick *m_thread_spec_up = *incoming.m_thread_spec_up;
220061da546Spatrick m_set_flags.Set(eThreadSpec);
221061da546Spatrick }
222061da546Spatrick }
223061da546Spatrick
224061da546Spatrick // Destructor
225061da546Spatrick BreakpointOptions::~BreakpointOptions() = default;
226061da546Spatrick
CreateFromStructuredData(Target & target,const StructuredData::Dictionary & options_dict,Status & error)227061da546Spatrick std::unique_ptr<BreakpointOptions> BreakpointOptions::CreateFromStructuredData(
228061da546Spatrick Target &target, const StructuredData::Dictionary &options_dict,
229061da546Spatrick Status &error) {
230061da546Spatrick bool enabled = true;
231061da546Spatrick bool one_shot = false;
232061da546Spatrick bool auto_continue = false;
233061da546Spatrick int32_t ignore_count = 0;
234061da546Spatrick llvm::StringRef condition_ref("");
235061da546Spatrick Flags set_options;
236061da546Spatrick
237061da546Spatrick const char *key = GetKey(OptionNames::EnabledState);
238061da546Spatrick bool success;
239061da546Spatrick if (key && options_dict.HasKey(key)) {
240061da546Spatrick success = options_dict.GetValueForKeyAsBoolean(key, enabled);
241061da546Spatrick if (!success) {
242061da546Spatrick error.SetErrorStringWithFormat("%s key is not a boolean.", key);
243061da546Spatrick return nullptr;
244061da546Spatrick }
245061da546Spatrick set_options.Set(eEnabled);
246061da546Spatrick }
247061da546Spatrick
248061da546Spatrick key = GetKey(OptionNames::OneShotState);
249061da546Spatrick if (key && options_dict.HasKey(key)) {
250061da546Spatrick success = options_dict.GetValueForKeyAsBoolean(key, one_shot);
251061da546Spatrick if (!success) {
252061da546Spatrick error.SetErrorStringWithFormat("%s key is not a boolean.", key);
253061da546Spatrick return nullptr;
254061da546Spatrick }
255061da546Spatrick set_options.Set(eOneShot);
256061da546Spatrick }
257061da546Spatrick
258061da546Spatrick key = GetKey(OptionNames::AutoContinue);
259061da546Spatrick if (key && options_dict.HasKey(key)) {
260061da546Spatrick success = options_dict.GetValueForKeyAsBoolean(key, auto_continue);
261061da546Spatrick if (!success) {
262061da546Spatrick error.SetErrorStringWithFormat("%s key is not a boolean.", key);
263061da546Spatrick return nullptr;
264061da546Spatrick }
265061da546Spatrick set_options.Set(eAutoContinue);
266061da546Spatrick }
267061da546Spatrick
268061da546Spatrick key = GetKey(OptionNames::IgnoreCount);
269061da546Spatrick if (key && options_dict.HasKey(key)) {
270061da546Spatrick success = options_dict.GetValueForKeyAsInteger(key, ignore_count);
271061da546Spatrick if (!success) {
272061da546Spatrick error.SetErrorStringWithFormat("%s key is not an integer.", key);
273061da546Spatrick return nullptr;
274061da546Spatrick }
275061da546Spatrick set_options.Set(eIgnoreCount);
276061da546Spatrick }
277061da546Spatrick
278061da546Spatrick key = GetKey(OptionNames::ConditionText);
279061da546Spatrick if (key && options_dict.HasKey(key)) {
280061da546Spatrick success = options_dict.GetValueForKeyAsString(key, condition_ref);
281061da546Spatrick if (!success) {
282061da546Spatrick error.SetErrorStringWithFormat("%s key is not an string.", key);
283061da546Spatrick return nullptr;
284061da546Spatrick }
285061da546Spatrick set_options.Set(eCondition);
286061da546Spatrick }
287061da546Spatrick
288061da546Spatrick std::unique_ptr<CommandData> cmd_data_up;
289061da546Spatrick StructuredData::Dictionary *cmds_dict;
290061da546Spatrick success = options_dict.GetValueForKeyAsDictionary(
291061da546Spatrick CommandData::GetSerializationKey(), cmds_dict);
292061da546Spatrick if (success && cmds_dict) {
293061da546Spatrick Status cmds_error;
294061da546Spatrick cmd_data_up = CommandData::CreateFromStructuredData(*cmds_dict, cmds_error);
295061da546Spatrick if (cmds_error.Fail()) {
296061da546Spatrick error.SetErrorStringWithFormat(
297061da546Spatrick "Failed to deserialize breakpoint command options: %s.",
298061da546Spatrick cmds_error.AsCString());
299061da546Spatrick return nullptr;
300061da546Spatrick }
301061da546Spatrick }
302061da546Spatrick
303061da546Spatrick auto bp_options = std::make_unique<BreakpointOptions>(
304061da546Spatrick condition_ref.str().c_str(), enabled,
305061da546Spatrick ignore_count, one_shot, auto_continue);
306061da546Spatrick if (cmd_data_up) {
307061da546Spatrick if (cmd_data_up->interpreter == eScriptLanguageNone)
308061da546Spatrick bp_options->SetCommandDataCallback(cmd_data_up);
309061da546Spatrick else {
310061da546Spatrick ScriptInterpreter *interp = target.GetDebugger().GetScriptInterpreter();
311061da546Spatrick if (!interp) {
312be691f3bSpatrick error.SetErrorString(
313061da546Spatrick "Can't set script commands - no script interpreter");
314061da546Spatrick return nullptr;
315061da546Spatrick }
316061da546Spatrick if (interp->GetLanguage() != cmd_data_up->interpreter) {
317061da546Spatrick error.SetErrorStringWithFormat(
318061da546Spatrick "Current script language doesn't match breakpoint's language: %s",
319061da546Spatrick ScriptInterpreter::LanguageToString(cmd_data_up->interpreter)
320061da546Spatrick .c_str());
321061da546Spatrick return nullptr;
322061da546Spatrick }
323061da546Spatrick Status script_error;
324061da546Spatrick script_error =
325be691f3bSpatrick interp->SetBreakpointCommandCallback(*bp_options, cmd_data_up);
326061da546Spatrick if (script_error.Fail()) {
327061da546Spatrick error.SetErrorStringWithFormat("Error generating script callback: %s.",
328061da546Spatrick error.AsCString());
329061da546Spatrick return nullptr;
330061da546Spatrick }
331061da546Spatrick }
332061da546Spatrick }
333061da546Spatrick
334061da546Spatrick StructuredData::Dictionary *thread_spec_dict;
335061da546Spatrick success = options_dict.GetValueForKeyAsDictionary(
336061da546Spatrick ThreadSpec::GetSerializationKey(), thread_spec_dict);
337061da546Spatrick if (success) {
338061da546Spatrick Status thread_spec_error;
339061da546Spatrick std::unique_ptr<ThreadSpec> thread_spec_up =
340061da546Spatrick ThreadSpec::CreateFromStructuredData(*thread_spec_dict,
341061da546Spatrick thread_spec_error);
342061da546Spatrick if (thread_spec_error.Fail()) {
343061da546Spatrick error.SetErrorStringWithFormat(
344061da546Spatrick "Failed to deserialize breakpoint thread spec options: %s.",
345061da546Spatrick thread_spec_error.AsCString());
346061da546Spatrick return nullptr;
347061da546Spatrick }
348061da546Spatrick bp_options->SetThreadSpec(thread_spec_up);
349061da546Spatrick }
350061da546Spatrick return bp_options;
351061da546Spatrick }
352061da546Spatrick
SerializeToStructuredData()353061da546Spatrick StructuredData::ObjectSP BreakpointOptions::SerializeToStructuredData() {
354061da546Spatrick StructuredData::DictionarySP options_dict_sp(
355061da546Spatrick new StructuredData::Dictionary());
356061da546Spatrick if (m_set_flags.Test(eEnabled))
357061da546Spatrick options_dict_sp->AddBooleanItem(GetKey(OptionNames::EnabledState),
358061da546Spatrick m_enabled);
359061da546Spatrick if (m_set_flags.Test(eOneShot))
360061da546Spatrick options_dict_sp->AddBooleanItem(GetKey(OptionNames::OneShotState),
361061da546Spatrick m_one_shot);
362061da546Spatrick if (m_set_flags.Test(eAutoContinue))
363061da546Spatrick options_dict_sp->AddBooleanItem(GetKey(OptionNames::AutoContinue),
364061da546Spatrick m_auto_continue);
365061da546Spatrick if (m_set_flags.Test(eIgnoreCount))
366061da546Spatrick options_dict_sp->AddIntegerItem(GetKey(OptionNames::IgnoreCount),
367061da546Spatrick m_ignore_count);
368061da546Spatrick if (m_set_flags.Test(eCondition))
369061da546Spatrick options_dict_sp->AddStringItem(GetKey(OptionNames::ConditionText),
370061da546Spatrick m_condition_text);
371061da546Spatrick
372061da546Spatrick if (m_set_flags.Test(eCallback) && m_baton_is_command_baton) {
373061da546Spatrick auto cmd_baton =
374061da546Spatrick std::static_pointer_cast<CommandBaton>(m_callback_baton_sp);
375061da546Spatrick StructuredData::ObjectSP commands_sp =
376061da546Spatrick cmd_baton->getItem()->SerializeToStructuredData();
377061da546Spatrick if (commands_sp) {
378061da546Spatrick options_dict_sp->AddItem(
379061da546Spatrick BreakpointOptions::CommandData::GetSerializationKey(), commands_sp);
380061da546Spatrick }
381061da546Spatrick }
382061da546Spatrick if (m_set_flags.Test(eThreadSpec) && m_thread_spec_up) {
383061da546Spatrick StructuredData::ObjectSP thread_spec_sp =
384061da546Spatrick m_thread_spec_up->SerializeToStructuredData();
385061da546Spatrick options_dict_sp->AddItem(ThreadSpec::GetSerializationKey(), thread_spec_sp);
386061da546Spatrick }
387061da546Spatrick
388061da546Spatrick return options_dict_sp;
389061da546Spatrick }
390061da546Spatrick
391061da546Spatrick // Callbacks
SetCallback(BreakpointHitCallback callback,const lldb::BatonSP & callback_baton_sp,bool callback_is_synchronous)392061da546Spatrick void BreakpointOptions::SetCallback(BreakpointHitCallback callback,
393061da546Spatrick const lldb::BatonSP &callback_baton_sp,
394061da546Spatrick bool callback_is_synchronous) {
395061da546Spatrick // FIXME: This seems unsafe. If BatonSP actually *is* a CommandBaton, but
396061da546Spatrick // in a shared_ptr<Baton> instead of a shared_ptr<CommandBaton>, then we will
397061da546Spatrick // set m_baton_is_command_baton to false, which is incorrect. One possible
398061da546Spatrick // solution is to make the base Baton class provide a method such as:
399061da546Spatrick // virtual StringRef getBatonId() const { return ""; }
400061da546Spatrick // and have CommandBaton override this to return something unique, and then
401061da546Spatrick // check for it here. Another option might be to make Baton using the llvm
402061da546Spatrick // casting infrastructure, so that we could write something like:
403061da546Spatrick // if (llvm::isa<CommandBaton>(callback_baton_sp))
404061da546Spatrick // at relevant callsites instead of storing a boolean.
405061da546Spatrick m_callback_is_synchronous = callback_is_synchronous;
406061da546Spatrick m_callback = callback;
407061da546Spatrick m_callback_baton_sp = callback_baton_sp;
408061da546Spatrick m_baton_is_command_baton = false;
409061da546Spatrick m_set_flags.Set(eCallback);
410061da546Spatrick }
411061da546Spatrick
SetCallback(BreakpointHitCallback callback,const BreakpointOptions::CommandBatonSP & callback_baton_sp,bool callback_is_synchronous)412061da546Spatrick void BreakpointOptions::SetCallback(
413061da546Spatrick BreakpointHitCallback callback,
414061da546Spatrick const BreakpointOptions::CommandBatonSP &callback_baton_sp,
415061da546Spatrick bool callback_is_synchronous) {
416061da546Spatrick m_callback_is_synchronous = callback_is_synchronous;
417061da546Spatrick m_callback = callback;
418061da546Spatrick m_callback_baton_sp = callback_baton_sp;
419061da546Spatrick m_baton_is_command_baton = true;
420061da546Spatrick m_set_flags.Set(eCallback);
421061da546Spatrick }
422061da546Spatrick
ClearCallback()423061da546Spatrick void BreakpointOptions::ClearCallback() {
424061da546Spatrick m_callback = BreakpointOptions::NullCallback;
425061da546Spatrick m_callback_is_synchronous = false;
426061da546Spatrick m_callback_baton_sp.reset();
427061da546Spatrick m_baton_is_command_baton = false;
428061da546Spatrick m_set_flags.Clear(eCallback);
429061da546Spatrick }
430061da546Spatrick
GetBaton()431061da546Spatrick Baton *BreakpointOptions::GetBaton() { return m_callback_baton_sp.get(); }
432061da546Spatrick
GetBaton() const433061da546Spatrick const Baton *BreakpointOptions::GetBaton() const {
434061da546Spatrick return m_callback_baton_sp.get();
435061da546Spatrick }
436061da546Spatrick
InvokeCallback(StoppointCallbackContext * context,lldb::user_id_t break_id,lldb::user_id_t break_loc_id)437061da546Spatrick bool BreakpointOptions::InvokeCallback(StoppointCallbackContext *context,
438061da546Spatrick lldb::user_id_t break_id,
439061da546Spatrick lldb::user_id_t break_loc_id) {
440061da546Spatrick if (m_callback) {
441061da546Spatrick if (context->is_synchronous == IsCallbackSynchronous()) {
442061da546Spatrick return m_callback(m_callback_baton_sp ? m_callback_baton_sp->data()
443061da546Spatrick : nullptr,
444061da546Spatrick context, break_id, break_loc_id);
445061da546Spatrick } else if (IsCallbackSynchronous()) {
446061da546Spatrick return false;
447061da546Spatrick }
448061da546Spatrick }
449061da546Spatrick return true;
450061da546Spatrick }
451061da546Spatrick
HasCallback() const452061da546Spatrick bool BreakpointOptions::HasCallback() const {
453061da546Spatrick return m_callback != BreakpointOptions::NullCallback;
454061da546Spatrick }
455061da546Spatrick
GetCommandLineCallbacks(StringList & command_list)456061da546Spatrick bool BreakpointOptions::GetCommandLineCallbacks(StringList &command_list) {
457061da546Spatrick if (!HasCallback())
458061da546Spatrick return false;
459061da546Spatrick if (!m_baton_is_command_baton)
460061da546Spatrick return false;
461061da546Spatrick
462061da546Spatrick auto cmd_baton = std::static_pointer_cast<CommandBaton>(m_callback_baton_sp);
463061da546Spatrick CommandData *data = cmd_baton->getItem();
464061da546Spatrick if (!data)
465061da546Spatrick return false;
466061da546Spatrick command_list = data->user_source;
467061da546Spatrick return true;
468061da546Spatrick }
469061da546Spatrick
SetCondition(const char * condition)470061da546Spatrick void BreakpointOptions::SetCondition(const char *condition) {
471061da546Spatrick if (!condition || condition[0] == '\0') {
472061da546Spatrick condition = "";
473061da546Spatrick m_set_flags.Clear(eCondition);
474061da546Spatrick }
475061da546Spatrick else
476061da546Spatrick m_set_flags.Set(eCondition);
477061da546Spatrick
478061da546Spatrick m_condition_text.assign(condition);
479061da546Spatrick std::hash<std::string> hasher;
480061da546Spatrick m_condition_text_hash = hasher(m_condition_text);
481061da546Spatrick }
482061da546Spatrick
GetConditionText(size_t * hash) const483061da546Spatrick const char *BreakpointOptions::GetConditionText(size_t *hash) const {
484061da546Spatrick if (!m_condition_text.empty()) {
485061da546Spatrick if (hash)
486061da546Spatrick *hash = m_condition_text_hash;
487061da546Spatrick
488061da546Spatrick return m_condition_text.c_str();
489061da546Spatrick } else {
490061da546Spatrick return nullptr;
491061da546Spatrick }
492061da546Spatrick }
493061da546Spatrick
GetThreadSpecNoCreate() const494061da546Spatrick const ThreadSpec *BreakpointOptions::GetThreadSpecNoCreate() const {
495061da546Spatrick return m_thread_spec_up.get();
496061da546Spatrick }
497061da546Spatrick
GetThreadSpec()498061da546Spatrick ThreadSpec *BreakpointOptions::GetThreadSpec() {
499061da546Spatrick if (m_thread_spec_up == nullptr) {
500061da546Spatrick m_set_flags.Set(eThreadSpec);
501dda28197Spatrick m_thread_spec_up = std::make_unique<ThreadSpec>();
502061da546Spatrick }
503061da546Spatrick
504061da546Spatrick return m_thread_spec_up.get();
505061da546Spatrick }
506061da546Spatrick
SetThreadID(lldb::tid_t thread_id)507061da546Spatrick void BreakpointOptions::SetThreadID(lldb::tid_t thread_id) {
508061da546Spatrick GetThreadSpec()->SetTID(thread_id);
509061da546Spatrick m_set_flags.Set(eThreadSpec);
510061da546Spatrick }
511061da546Spatrick
SetThreadSpec(std::unique_ptr<ThreadSpec> & thread_spec_up)512061da546Spatrick void BreakpointOptions::SetThreadSpec(
513061da546Spatrick std::unique_ptr<ThreadSpec> &thread_spec_up) {
514061da546Spatrick m_thread_spec_up = std::move(thread_spec_up);
515061da546Spatrick m_set_flags.Set(eThreadSpec);
516061da546Spatrick }
517061da546Spatrick
GetDescription(Stream * s,lldb::DescriptionLevel level) const518061da546Spatrick void BreakpointOptions::GetDescription(Stream *s,
519061da546Spatrick lldb::DescriptionLevel level) const {
520061da546Spatrick // Figure out if there are any options not at their default value, and only
521061da546Spatrick // print anything if there are:
522061da546Spatrick
523061da546Spatrick if (m_ignore_count != 0 || !m_enabled || m_one_shot || m_auto_continue ||
524061da546Spatrick (GetThreadSpecNoCreate() != nullptr &&
525061da546Spatrick GetThreadSpecNoCreate()->HasSpecification())) {
526061da546Spatrick if (level == lldb::eDescriptionLevelVerbose) {
527061da546Spatrick s->EOL();
528061da546Spatrick s->IndentMore();
529061da546Spatrick s->Indent();
530061da546Spatrick s->PutCString("Breakpoint Options:\n");
531061da546Spatrick s->IndentMore();
532061da546Spatrick s->Indent();
533061da546Spatrick } else
534061da546Spatrick s->PutCString(" Options: ");
535061da546Spatrick
536061da546Spatrick if (m_ignore_count > 0)
537061da546Spatrick s->Printf("ignore: %d ", m_ignore_count);
538061da546Spatrick s->Printf("%sabled ", m_enabled ? "en" : "dis");
539061da546Spatrick
540061da546Spatrick if (m_one_shot)
541061da546Spatrick s->Printf("one-shot ");
542061da546Spatrick
543061da546Spatrick if (m_auto_continue)
544061da546Spatrick s->Printf("auto-continue ");
545061da546Spatrick
546061da546Spatrick if (m_thread_spec_up)
547061da546Spatrick m_thread_spec_up->GetDescription(s, level);
548061da546Spatrick
549061da546Spatrick if (level == lldb::eDescriptionLevelFull) {
550061da546Spatrick s->IndentLess();
551061da546Spatrick s->IndentMore();
552061da546Spatrick }
553061da546Spatrick }
554061da546Spatrick
555061da546Spatrick if (m_callback_baton_sp.get()) {
556061da546Spatrick if (level != eDescriptionLevelBrief) {
557061da546Spatrick s->EOL();
558061da546Spatrick m_callback_baton_sp->GetDescription(s->AsRawOstream(), level,
559061da546Spatrick s->GetIndentLevel());
560061da546Spatrick }
561061da546Spatrick }
562061da546Spatrick if (!m_condition_text.empty()) {
563061da546Spatrick if (level != eDescriptionLevelBrief) {
564061da546Spatrick s->EOL();
565061da546Spatrick s->Printf("Condition: %s\n", m_condition_text.c_str());
566061da546Spatrick }
567061da546Spatrick }
568061da546Spatrick }
569061da546Spatrick
GetDescription(llvm::raw_ostream & s,lldb::DescriptionLevel level,unsigned indentation) const570061da546Spatrick void BreakpointOptions::CommandBaton::GetDescription(
571061da546Spatrick llvm::raw_ostream &s, lldb::DescriptionLevel level,
572061da546Spatrick unsigned indentation) const {
573061da546Spatrick const CommandData *data = getItem();
574061da546Spatrick
575061da546Spatrick if (level == eDescriptionLevelBrief) {
576061da546Spatrick s << ", commands = "
577061da546Spatrick << ((data && data->user_source.GetSize() > 0) ? "yes" : "no");
578061da546Spatrick return;
579061da546Spatrick }
580061da546Spatrick
581061da546Spatrick indentation += 2;
582061da546Spatrick s.indent(indentation);
583061da546Spatrick s << "Breakpoint commands";
584061da546Spatrick if (data->interpreter != eScriptLanguageNone)
585061da546Spatrick s << llvm::formatv(" ({0}):\n",
586061da546Spatrick ScriptInterpreter::LanguageToString(data->interpreter));
587061da546Spatrick else
588061da546Spatrick s << ":\n";
589061da546Spatrick
590061da546Spatrick indentation += 2;
591061da546Spatrick if (data && data->user_source.GetSize() > 0) {
592061da546Spatrick for (llvm::StringRef str : data->user_source) {
593061da546Spatrick s.indent(indentation);
594061da546Spatrick s << str << "\n";
595061da546Spatrick }
596061da546Spatrick } else
597061da546Spatrick s << "No commands.\n";
598061da546Spatrick }
599061da546Spatrick
SetCommandDataCallback(std::unique_ptr<CommandData> & cmd_data)600061da546Spatrick void BreakpointOptions::SetCommandDataCallback(
601061da546Spatrick std::unique_ptr<CommandData> &cmd_data) {
602061da546Spatrick cmd_data->interpreter = eScriptLanguageNone;
603061da546Spatrick auto baton_sp = std::make_shared<CommandBaton>(std::move(cmd_data));
604061da546Spatrick SetCallback(BreakpointOptions::BreakpointOptionsCallbackFunction, baton_sp);
605061da546Spatrick m_set_flags.Set(eCallback);
606061da546Spatrick }
607061da546Spatrick
BreakpointOptionsCallbackFunction(void * baton,StoppointCallbackContext * context,lldb::user_id_t break_id,lldb::user_id_t break_loc_id)608061da546Spatrick bool BreakpointOptions::BreakpointOptionsCallbackFunction(
609061da546Spatrick void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id,
610061da546Spatrick lldb::user_id_t break_loc_id) {
611061da546Spatrick bool ret_value = true;
612061da546Spatrick if (baton == nullptr)
613061da546Spatrick return true;
614061da546Spatrick
615061da546Spatrick CommandData *data = (CommandData *)baton;
616061da546Spatrick StringList &commands = data->user_source;
617061da546Spatrick
618061da546Spatrick if (commands.GetSize() > 0) {
619061da546Spatrick ExecutionContext exe_ctx(context->exe_ctx_ref);
620061da546Spatrick Target *target = exe_ctx.GetTargetPtr();
621061da546Spatrick if (target) {
622061da546Spatrick Debugger &debugger = target->GetDebugger();
623dda28197Spatrick CommandReturnObject result(debugger.GetUseColor());
624dda28197Spatrick
625061da546Spatrick // Rig up the results secondary output stream to the debugger's, so the
626061da546Spatrick // output will come out synchronously if the debugger is set up that way.
627061da546Spatrick StreamSP output_stream(debugger.GetAsyncOutputStream());
628061da546Spatrick StreamSP error_stream(debugger.GetAsyncErrorStream());
629061da546Spatrick result.SetImmediateOutputStream(output_stream);
630061da546Spatrick result.SetImmediateErrorStream(error_stream);
631061da546Spatrick
632061da546Spatrick CommandInterpreterRunOptions options;
633061da546Spatrick options.SetStopOnContinue(true);
634061da546Spatrick options.SetStopOnError(data->stop_on_error);
635061da546Spatrick options.SetEchoCommands(true);
636061da546Spatrick options.SetPrintResults(true);
637061da546Spatrick options.SetPrintErrors(true);
638061da546Spatrick options.SetAddToHistory(false);
639061da546Spatrick
640be691f3bSpatrick debugger.GetCommandInterpreter().HandleCommands(commands, exe_ctx,
641061da546Spatrick options, result);
642061da546Spatrick result.GetImmediateOutputStream()->Flush();
643061da546Spatrick result.GetImmediateErrorStream()->Flush();
644061da546Spatrick }
645061da546Spatrick }
646061da546Spatrick return ret_value;
647061da546Spatrick }
648061da546Spatrick
Clear()649061da546Spatrick void BreakpointOptions::Clear()
650061da546Spatrick {
651061da546Spatrick m_set_flags.Clear();
652061da546Spatrick m_thread_spec_up.release();
653061da546Spatrick m_one_shot = false;
654061da546Spatrick m_ignore_count = 0;
655061da546Spatrick m_auto_continue = false;
656061da546Spatrick m_callback = nullptr;
657061da546Spatrick m_callback_baton_sp.reset();
658061da546Spatrick m_baton_is_command_baton = false;
659061da546Spatrick m_callback_is_synchronous = false;
660061da546Spatrick m_enabled = false;
661061da546Spatrick m_condition_text.clear();
662061da546Spatrick }
663