1 //===-- BreakpointOptions.cpp -----------------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 // C Includes
11 // C++ Includes
12 // Other libraries and framework includes
13 // Project includes
14 #include "lldb/Breakpoint/BreakpointOptions.h"
15 
16 #include "lldb/Breakpoint/StoppointCallbackContext.h"
17 #include "lldb/Core/Stream.h"
18 #include "lldb/Core/StringList.h"
19 #include "lldb/Core/Value.h"
20 #include "lldb/Interpreter/CommandInterpreter.h"
21 #include "lldb/Interpreter/CommandReturnObject.h"
22 #include "lldb/Target/Process.h"
23 #include "lldb/Target/Target.h"
24 #include "lldb/Target/ThreadSpec.h"
25 
26 #include "llvm/ADT/STLExtras.h"
27 
28 using namespace lldb;
29 using namespace lldb_private;
30 
31 const char
32     *BreakpointOptions::CommandData::g_option_names[static_cast<uint32_t>(
33         BreakpointOptions::CommandData::OptionNames::LastOptionName)]{
34         "UserSource", "ScriptSource", "StopOnError"};
35 
36 StructuredData::ObjectSP
37 BreakpointOptions::CommandData::SerializeToStructuredData() {
38   size_t num_strings = user_source.GetSize();
39   if (num_strings == 0 && script_source.empty()) {
40     // We shouldn't serialize commands if there aren't any, return an empty sp
41     // to indicate this.
42     return StructuredData::ObjectSP();
43   }
44 
45   StructuredData::DictionarySP options_dict_sp(
46       new StructuredData::Dictionary());
47   options_dict_sp->AddBooleanItem(GetKey(OptionNames::StopOnError),
48                                   stop_on_error);
49 
50   StructuredData::ArraySP user_source_sp(new StructuredData::Array());
51   for (size_t i = 0; i < num_strings; i++) {
52     StructuredData::StringSP item_sp(
53         new StructuredData::String(user_source[i]));
54     user_source_sp->AddItem(item_sp);
55     options_dict_sp->AddItem(GetKey(OptionNames::UserSource), user_source_sp);
56   }
57 
58   if (!script_source.empty()) {
59     StructuredData::StringSP item_sp(new StructuredData::String(script_source));
60     options_dict_sp->AddItem(GetKey(OptionNames::ScriptSource), user_source_sp);
61   }
62   return options_dict_sp;
63 }
64 
65 std::unique_ptr<BreakpointOptions::CommandData>
66 BreakpointOptions::CommandData::CreateFromStructuredData(
67     const StructuredData::Dictionary &options_dict, Error &error) {
68   std::string script_source;
69   std::unique_ptr<CommandData> data_up(new CommandData());
70   bool found_something = false;
71 
72   bool success = options_dict.GetValueForKeyAsBoolean(
73       GetKey(OptionNames::StopOnError), data_up->stop_on_error);
74 
75   if (success)
76     found_something = true;
77 
78   success = options_dict.GetValueForKeyAsString(
79       GetKey(OptionNames::ScriptSource), data_up->script_source);
80 
81   if (success)
82     found_something = true;
83 
84   StructuredData::Array *user_source;
85   success = options_dict.GetValueForKeyAsArray(GetKey(OptionNames::UserSource),
86                                                user_source);
87   if (success) {
88     found_something = true;
89     size_t num_elems = user_source->GetSize();
90     for (size_t i = 0; i < num_elems; i++) {
91       std::string elem_string;
92       success = user_source->GetItemAtIndexAsString(i, elem_string);
93       if (success)
94         data_up->user_source.AppendString(elem_string);
95     }
96   }
97 
98   if (found_something)
99     return data_up;
100   else
101     return std::unique_ptr<BreakpointOptions::CommandData>();
102 }
103 
104 const char *BreakpointOptions::g_option_names
105     [BreakpointOptions::OptionNames::LastOptionName]{
106         "ConditionText", "IgnoreCount", "EnabledState", "OneShotState"};
107 
108 bool BreakpointOptions::NullCallback(void *baton,
109                                      StoppointCallbackContext *context,
110                                      lldb::user_id_t break_id,
111                                      lldb::user_id_t break_loc_id) {
112   return true;
113 }
114 
115 //----------------------------------------------------------------------
116 // BreakpointOptions constructor
117 //----------------------------------------------------------------------
118 BreakpointOptions::BreakpointOptions()
119     : m_callback(BreakpointOptions::NullCallback), m_callback_baton_sp(),
120       m_baton_is_command_baton(false), m_callback_is_synchronous(false),
121       m_enabled(true), m_one_shot(false), m_ignore_count(0), m_thread_spec_ap(),
122       m_condition_text(), m_condition_text_hash(0) {}
123 
124 BreakpointOptions::BreakpointOptions(const char *condition, bool enabled,
125                                      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(condition),
129       m_condition_text_hash(0)
130 
131 {}
132 
133 //----------------------------------------------------------------------
134 // BreakpointOptions copy constructor
135 //----------------------------------------------------------------------
136 BreakpointOptions::BreakpointOptions(const BreakpointOptions &rhs)
137     : m_callback(rhs.m_callback), m_callback_baton_sp(rhs.m_callback_baton_sp),
138       m_baton_is_command_baton(rhs.m_baton_is_command_baton),
139       m_callback_is_synchronous(rhs.m_callback_is_synchronous),
140       m_enabled(rhs.m_enabled), m_one_shot(rhs.m_one_shot),
141       m_ignore_count(rhs.m_ignore_count), m_thread_spec_ap() {
142   if (rhs.m_thread_spec_ap.get() != nullptr)
143     m_thread_spec_ap.reset(new ThreadSpec(*rhs.m_thread_spec_ap.get()));
144   m_condition_text = rhs.m_condition_text;
145   m_condition_text_hash = rhs.m_condition_text_hash;
146 }
147 
148 //----------------------------------------------------------------------
149 // BreakpointOptions assignment operator
150 //----------------------------------------------------------------------
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_ap.get() != nullptr)
161     m_thread_spec_ap.reset(new ThreadSpec(*rhs.m_thread_spec_ap.get()));
162   m_condition_text = rhs.m_condition_text;
163   m_condition_text_hash = rhs.m_condition_text_hash;
164   return *this;
165 }
166 
167 BreakpointOptions *
168 BreakpointOptions::CopyOptionsNoCallback(BreakpointOptions &orig) {
169   BreakpointHitCallback orig_callback = orig.m_callback;
170   lldb::BatonSP orig_callback_baton_sp = orig.m_callback_baton_sp;
171   bool orig_is_sync = orig.m_callback_is_synchronous;
172 
173   orig.ClearCallback();
174   BreakpointOptions *ret_val = new BreakpointOptions(orig);
175 
176   orig.SetCallback(orig_callback, orig_callback_baton_sp, orig_is_sync);
177 
178   return ret_val;
179 }
180 
181 //----------------------------------------------------------------------
182 // Destructor
183 //----------------------------------------------------------------------
184 BreakpointOptions::~BreakpointOptions() = default;
185 
186 std::unique_ptr<BreakpointOptions> BreakpointOptions::CreateFromStructuredData(
187     const StructuredData::Dictionary &options_dict, Error &error) {
188   bool enabled = true;
189   bool one_shot = false;
190   int32_t ignore_count = 0;
191   std::string condition_text;
192 
193   bool success = options_dict.GetValueForKeyAsBoolean(
194       GetKey(OptionNames::EnabledState), enabled);
195   if (!success) {
196     error.SetErrorStringWithFormat("%s key is not a boolean.",
197                                    GetKey(OptionNames::EnabledState));
198     return nullptr;
199   }
200 
201   success = options_dict.GetValueForKeyAsBoolean(
202       GetKey(OptionNames::OneShotState), one_shot);
203   if (!success) {
204     error.SetErrorStringWithFormat("%s key is not a boolean.",
205                                    GetKey(OptionNames::OneShotState));
206     return nullptr;
207   }
208   success = options_dict.GetValueForKeyAsInteger(
209       GetKey(OptionNames::IgnoreCount), ignore_count);
210   if (!success) {
211     error.SetErrorStringWithFormat("%s key is not an integer.",
212                                    GetKey(OptionNames::IgnoreCount));
213     return nullptr;
214   }
215 
216   std::unique_ptr<CommandData> cmd_data_up;
217   StructuredData::Dictionary *cmds_dict;
218   success = options_dict.GetValueForKeyAsDictionary(
219       CommandData::GetSerializationKey(), cmds_dict);
220   if (success && cmds_dict) {
221     Error cmds_error;
222     cmd_data_up = CommandData::CreateFromStructuredData(*cmds_dict, cmds_error);
223     if (cmds_error.Fail()) {
224       error.SetErrorStringWithFormat(
225           "Failed to deserialize breakpoint command options: %s.",
226           cmds_error.AsCString());
227       return nullptr;
228     }
229   }
230 
231   auto bp_options = llvm::make_unique<BreakpointOptions>(
232       condition_text.c_str(), enabled, ignore_count, one_shot);
233   if (cmd_data_up.get())
234     bp_options->SetCommandDataCallback(cmd_data_up);
235   return bp_options;
236 }
237 
238 StructuredData::ObjectSP BreakpointOptions::SerializeToStructuredData() {
239   StructuredData::DictionarySP options_dict_sp(
240       new StructuredData::Dictionary());
241   options_dict_sp->AddBooleanItem(GetKey(OptionNames::EnabledState), m_enabled);
242   options_dict_sp->AddBooleanItem(GetKey(OptionNames::OneShotState),
243                                   m_one_shot);
244   options_dict_sp->AddIntegerItem(GetKey(OptionNames::IgnoreCount),
245                                   m_ignore_count);
246   options_dict_sp->AddStringItem(GetKey(OptionNames::ConditionText),
247                                  m_condition_text);
248   if (m_baton_is_command_baton) {
249     auto cmd_baton =
250         std::static_pointer_cast<CommandBaton>(m_callback_baton_sp);
251     StructuredData::ObjectSP commands_sp =
252         cmd_baton->getItem()->SerializeToStructuredData();
253     if (commands_sp) {
254       options_dict_sp->AddItem(
255           BreakpointOptions::CommandData::GetSerializationKey(), commands_sp);
256     }
257   }
258   // FIXME: Need to serialize thread filter...
259   return options_dict_sp;
260 }
261 
262 //------------------------------------------------------------------
263 // Callbacks
264 //------------------------------------------------------------------
265 void BreakpointOptions::SetCallback(BreakpointHitCallback callback,
266                                     const lldb::BatonSP &callback_baton_sp,
267                                     bool callback_is_synchronous) {
268   // FIXME: This seems unsafe.  If BatonSP actually *is* a CommandBaton, but
269   // in a shared_ptr<Baton> instead of a shared_ptr<CommandBaton>, then we
270   // will set m_baton_is_command_baton to false, which is incorrect.
271   // One possible solution is to make the base Baton class provide a method
272   // such as:
273   //     virtual StringRef getBatonId() const { return ""; }
274   // and have CommandBaton override this to return something unique, and then
275   // check for it here.  Another option might be to make Baton using the llvm
276   // casting infrastructure, so that we could write something like:
277   //     if (llvm::isa<CommandBaton>(callback_baton_sp))
278   // at relevant callsites instead of storing a boolean.
279   m_callback_is_synchronous = callback_is_synchronous;
280   m_callback = callback;
281   m_callback_baton_sp = callback_baton_sp;
282   m_baton_is_command_baton = false;
283 }
284 
285 void BreakpointOptions::SetCallback(
286     BreakpointHitCallback callback,
287     const BreakpointOptions::CommandBatonSP &callback_baton_sp,
288     bool callback_is_synchronous) {
289   m_callback_is_synchronous = callback_is_synchronous;
290   m_callback = callback;
291   m_callback_baton_sp = callback_baton_sp;
292   m_baton_is_command_baton = true;
293 }
294 
295 void BreakpointOptions::ClearCallback() {
296   m_callback = BreakpointOptions::NullCallback;
297   m_callback_is_synchronous = false;
298   m_callback_baton_sp.reset();
299   m_baton_is_command_baton = false;
300 }
301 
302 Baton *BreakpointOptions::GetBaton() { return m_callback_baton_sp.get(); }
303 
304 const Baton *BreakpointOptions::GetBaton() const {
305   return m_callback_baton_sp.get();
306 }
307 
308 bool BreakpointOptions::InvokeCallback(StoppointCallbackContext *context,
309                                        lldb::user_id_t break_id,
310                                        lldb::user_id_t break_loc_id) {
311   if (m_callback && context->is_synchronous == IsCallbackSynchronous()) {
312     return m_callback(m_callback_baton_sp ? m_callback_baton_sp->data()
313                                           : nullptr,
314                       context, break_id, break_loc_id);
315   } else
316     return true;
317 }
318 
319 bool BreakpointOptions::HasCallback() const {
320   return m_callback != BreakpointOptions::NullCallback;
321 }
322 
323 bool BreakpointOptions::GetCommandLineCallbacks(StringList &command_list) {
324   if (!HasCallback())
325     return false;
326   if (!m_baton_is_command_baton)
327     return false;
328 
329   auto cmd_baton = std::static_pointer_cast<CommandBaton>(m_callback_baton_sp);
330   CommandData *data = cmd_baton->getItem();
331   if (!data)
332     return false;
333   command_list = data->user_source;
334   return true;
335 }
336 
337 void BreakpointOptions::SetCondition(const char *condition) {
338   if (!condition)
339     condition = "";
340 
341   m_condition_text.assign(condition);
342   std::hash<std::string> hasher;
343   m_condition_text_hash = hasher(m_condition_text);
344 }
345 
346 const char *BreakpointOptions::GetConditionText(size_t *hash) const {
347   if (!m_condition_text.empty()) {
348     if (hash)
349       *hash = m_condition_text_hash;
350 
351     return m_condition_text.c_str();
352   } else {
353     return nullptr;
354   }
355 }
356 
357 const ThreadSpec *BreakpointOptions::GetThreadSpecNoCreate() const {
358   return m_thread_spec_ap.get();
359 }
360 
361 ThreadSpec *BreakpointOptions::GetThreadSpec() {
362   if (m_thread_spec_ap.get() == nullptr)
363     m_thread_spec_ap.reset(new ThreadSpec());
364 
365   return m_thread_spec_ap.get();
366 }
367 
368 void BreakpointOptions::SetThreadID(lldb::tid_t thread_id) {
369   GetThreadSpec()->SetTID(thread_id);
370 }
371 
372 void BreakpointOptions::GetDescription(Stream *s,
373                                        lldb::DescriptionLevel level) const {
374   // Figure out if there are any options not at their default value, and only
375   // print
376   // anything if there are:
377 
378   if (m_ignore_count != 0 || !m_enabled || m_one_shot ||
379       (GetThreadSpecNoCreate() != nullptr &&
380        GetThreadSpecNoCreate()->HasSpecification())) {
381     if (level == lldb::eDescriptionLevelVerbose) {
382       s->EOL();
383       s->IndentMore();
384       s->Indent();
385       s->PutCString("Breakpoint Options:\n");
386       s->IndentMore();
387       s->Indent();
388     } else
389       s->PutCString(" Options: ");
390 
391     if (m_ignore_count > 0)
392       s->Printf("ignore: %d ", m_ignore_count);
393     s->Printf("%sabled ", m_enabled ? "en" : "dis");
394 
395     if (m_one_shot)
396       s->Printf("one-shot ");
397 
398     if (m_thread_spec_ap.get())
399       m_thread_spec_ap->GetDescription(s, level);
400 
401     if (level == lldb::eDescriptionLevelFull) {
402       s->IndentLess();
403       s->IndentMore();
404     }
405   }
406 
407   if (m_callback_baton_sp.get()) {
408     if (level != eDescriptionLevelBrief) {
409       s->EOL();
410       m_callback_baton_sp->GetDescription(s, level);
411     }
412   }
413   if (!m_condition_text.empty()) {
414     if (level != eDescriptionLevelBrief) {
415       s->EOL();
416       s->Printf("Condition: %s\n", m_condition_text.c_str());
417     }
418   }
419 }
420 
421 void BreakpointOptions::CommandBaton::GetDescription(
422     Stream *s, lldb::DescriptionLevel level) const {
423   const CommandData *data = getItem();
424 
425   if (level == eDescriptionLevelBrief) {
426     s->Printf(", commands = %s",
427               (data && data->user_source.GetSize() > 0) ? "yes" : "no");
428     return;
429   }
430 
431   s->IndentMore();
432   s->Indent("Breakpoint commands:\n");
433 
434   s->IndentMore();
435   if (data && data->user_source.GetSize() > 0) {
436     const size_t num_strings = data->user_source.GetSize();
437     for (size_t i = 0; i < num_strings; ++i) {
438       s->Indent(data->user_source.GetStringAtIndex(i));
439       s->EOL();
440     }
441   } else {
442     s->PutCString("No commands.\n");
443   }
444   s->IndentLess();
445   s->IndentLess();
446 }
447 
448 void BreakpointOptions::SetCommandDataCallback(
449     std::unique_ptr<CommandData> &cmd_data) {
450   auto baton_sp = std::make_shared<CommandBaton>(std::move(cmd_data));
451   SetCallback(BreakpointOptions::BreakpointOptionsCallbackFunction, baton_sp);
452 }
453 
454 bool BreakpointOptions::BreakpointOptionsCallbackFunction(
455     void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id,
456     lldb::user_id_t break_loc_id) {
457   bool ret_value = true;
458   if (baton == nullptr)
459     return true;
460 
461   CommandData *data = (CommandData *)baton;
462   StringList &commands = data->user_source;
463 
464   if (commands.GetSize() > 0) {
465     ExecutionContext exe_ctx(context->exe_ctx_ref);
466     Target *target = exe_ctx.GetTargetPtr();
467     if (target) {
468       CommandReturnObject result;
469       Debugger &debugger = target->GetDebugger();
470       // Rig up the results secondary output stream to the debugger's, so the
471       // output will come out synchronously
472       // if the debugger is set up that way.
473 
474       StreamSP output_stream(debugger.GetAsyncOutputStream());
475       StreamSP error_stream(debugger.GetAsyncErrorStream());
476       result.SetImmediateOutputStream(output_stream);
477       result.SetImmediateErrorStream(error_stream);
478 
479       CommandInterpreterRunOptions options;
480       options.SetStopOnContinue(true);
481       options.SetStopOnError(data->stop_on_error);
482       options.SetEchoCommands(true);
483       options.SetPrintResults(true);
484       options.SetAddToHistory(false);
485 
486       debugger.GetCommandInterpreter().HandleCommands(commands, &exe_ctx,
487                                                       options, result);
488       result.GetImmediateOutputStream()->Flush();
489       result.GetImmediateErrorStream()->Flush();
490     }
491   }
492   return ret_value;
493 }
494