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     size_t)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 
236   StructuredData::Dictionary *thread_spec_dict;
237   success = options_dict.GetValueForKeyAsDictionary(
238       ThreadSpec::GetSerializationKey(), thread_spec_dict);
239   if (success) {
240     Error thread_spec_error;
241     std::unique_ptr<ThreadSpec> thread_spec_up =
242         ThreadSpec::CreateFromStructuredData(*thread_spec_dict,
243                                              thread_spec_error);
244     if (thread_spec_error.Fail()) {
245       error.SetErrorStringWithFormat(
246           "Failed to deserialize breakpoint thread spec options: %s.",
247           thread_spec_error.AsCString());
248       return nullptr;
249     }
250     bp_options->SetThreadSpec(thread_spec_up);
251   }
252   return bp_options;
253 }
254 
255 StructuredData::ObjectSP BreakpointOptions::SerializeToStructuredData() {
256   StructuredData::DictionarySP options_dict_sp(
257       new StructuredData::Dictionary());
258   options_dict_sp->AddBooleanItem(GetKey(OptionNames::EnabledState), m_enabled);
259   options_dict_sp->AddBooleanItem(GetKey(OptionNames::OneShotState),
260                                   m_one_shot);
261   options_dict_sp->AddIntegerItem(GetKey(OptionNames::IgnoreCount),
262                                   m_ignore_count);
263   options_dict_sp->AddStringItem(GetKey(OptionNames::ConditionText),
264                                  m_condition_text);
265   if (m_baton_is_command_baton) {
266     auto cmd_baton =
267         std::static_pointer_cast<CommandBaton>(m_callback_baton_sp);
268     StructuredData::ObjectSP commands_sp =
269         cmd_baton->getItem()->SerializeToStructuredData();
270     if (commands_sp) {
271       options_dict_sp->AddItem(
272           BreakpointOptions::CommandData::GetSerializationKey(), commands_sp);
273     }
274   }
275   if (m_thread_spec_ap) {
276     StructuredData::ObjectSP thread_spec_sp =
277         m_thread_spec_ap->SerializeToStructuredData();
278     options_dict_sp->AddItem(ThreadSpec::GetSerializationKey(), thread_spec_sp);
279   }
280 
281   return options_dict_sp;
282 }
283 
284 //------------------------------------------------------------------
285 // Callbacks
286 //------------------------------------------------------------------
287 void BreakpointOptions::SetCallback(BreakpointHitCallback callback,
288                                     const lldb::BatonSP &callback_baton_sp,
289                                     bool callback_is_synchronous) {
290   // FIXME: This seems unsafe.  If BatonSP actually *is* a CommandBaton, but
291   // in a shared_ptr<Baton> instead of a shared_ptr<CommandBaton>, then we
292   // will set m_baton_is_command_baton to false, which is incorrect.
293   // One possible solution is to make the base Baton class provide a method
294   // such as:
295   //     virtual StringRef getBatonId() const { return ""; }
296   // and have CommandBaton override this to return something unique, and then
297   // check for it here.  Another option might be to make Baton using the llvm
298   // casting infrastructure, so that we could write something like:
299   //     if (llvm::isa<CommandBaton>(callback_baton_sp))
300   // at relevant callsites instead of storing a boolean.
301   m_callback_is_synchronous = callback_is_synchronous;
302   m_callback = callback;
303   m_callback_baton_sp = callback_baton_sp;
304   m_baton_is_command_baton = false;
305 }
306 
307 void BreakpointOptions::SetCallback(
308     BreakpointHitCallback callback,
309     const BreakpointOptions::CommandBatonSP &callback_baton_sp,
310     bool callback_is_synchronous) {
311   m_callback_is_synchronous = callback_is_synchronous;
312   m_callback = callback;
313   m_callback_baton_sp = callback_baton_sp;
314   m_baton_is_command_baton = true;
315 }
316 
317 void BreakpointOptions::ClearCallback() {
318   m_callback = BreakpointOptions::NullCallback;
319   m_callback_is_synchronous = false;
320   m_callback_baton_sp.reset();
321   m_baton_is_command_baton = false;
322 }
323 
324 Baton *BreakpointOptions::GetBaton() { return m_callback_baton_sp.get(); }
325 
326 const Baton *BreakpointOptions::GetBaton() const {
327   return m_callback_baton_sp.get();
328 }
329 
330 bool BreakpointOptions::InvokeCallback(StoppointCallbackContext *context,
331                                        lldb::user_id_t break_id,
332                                        lldb::user_id_t break_loc_id) {
333   if (m_callback && context->is_synchronous == IsCallbackSynchronous()) {
334     return m_callback(m_callback_baton_sp ? m_callback_baton_sp->data()
335                                           : nullptr,
336                       context, break_id, break_loc_id);
337   } else
338     return true;
339 }
340 
341 bool BreakpointOptions::HasCallback() const {
342   return m_callback != BreakpointOptions::NullCallback;
343 }
344 
345 bool BreakpointOptions::GetCommandLineCallbacks(StringList &command_list) {
346   if (!HasCallback())
347     return false;
348   if (!m_baton_is_command_baton)
349     return false;
350 
351   auto cmd_baton = std::static_pointer_cast<CommandBaton>(m_callback_baton_sp);
352   CommandData *data = cmd_baton->getItem();
353   if (!data)
354     return false;
355   command_list = data->user_source;
356   return true;
357 }
358 
359 void BreakpointOptions::SetCondition(const char *condition) {
360   if (!condition)
361     condition = "";
362 
363   m_condition_text.assign(condition);
364   std::hash<std::string> hasher;
365   m_condition_text_hash = hasher(m_condition_text);
366 }
367 
368 const char *BreakpointOptions::GetConditionText(size_t *hash) const {
369   if (!m_condition_text.empty()) {
370     if (hash)
371       *hash = m_condition_text_hash;
372 
373     return m_condition_text.c_str();
374   } else {
375     return nullptr;
376   }
377 }
378 
379 const ThreadSpec *BreakpointOptions::GetThreadSpecNoCreate() const {
380   return m_thread_spec_ap.get();
381 }
382 
383 ThreadSpec *BreakpointOptions::GetThreadSpec() {
384   if (m_thread_spec_ap.get() == nullptr)
385     m_thread_spec_ap.reset(new ThreadSpec());
386 
387   return m_thread_spec_ap.get();
388 }
389 
390 void BreakpointOptions::SetThreadID(lldb::tid_t thread_id) {
391   GetThreadSpec()->SetTID(thread_id);
392 }
393 
394 void BreakpointOptions::SetThreadSpec(
395     std::unique_ptr<ThreadSpec> &thread_spec_up) {
396   m_thread_spec_ap = std::move(thread_spec_up);
397 }
398 
399 void BreakpointOptions::GetDescription(Stream *s,
400                                        lldb::DescriptionLevel level) const {
401   // Figure out if there are any options not at their default value, and only
402   // print
403   // anything if there are:
404 
405   if (m_ignore_count != 0 || !m_enabled || m_one_shot ||
406       (GetThreadSpecNoCreate() != nullptr &&
407        GetThreadSpecNoCreate()->HasSpecification())) {
408     if (level == lldb::eDescriptionLevelVerbose) {
409       s->EOL();
410       s->IndentMore();
411       s->Indent();
412       s->PutCString("Breakpoint Options:\n");
413       s->IndentMore();
414       s->Indent();
415     } else
416       s->PutCString(" Options: ");
417 
418     if (m_ignore_count > 0)
419       s->Printf("ignore: %d ", m_ignore_count);
420     s->Printf("%sabled ", m_enabled ? "en" : "dis");
421 
422     if (m_one_shot)
423       s->Printf("one-shot ");
424 
425     if (m_thread_spec_ap.get())
426       m_thread_spec_ap->GetDescription(s, level);
427 
428     if (level == lldb::eDescriptionLevelFull) {
429       s->IndentLess();
430       s->IndentMore();
431     }
432   }
433 
434   if (m_callback_baton_sp.get()) {
435     if (level != eDescriptionLevelBrief) {
436       s->EOL();
437       m_callback_baton_sp->GetDescription(s, level);
438     }
439   }
440   if (!m_condition_text.empty()) {
441     if (level != eDescriptionLevelBrief) {
442       s->EOL();
443       s->Printf("Condition: %s\n", m_condition_text.c_str());
444     }
445   }
446 }
447 
448 void BreakpointOptions::CommandBaton::GetDescription(
449     Stream *s, lldb::DescriptionLevel level) const {
450   const CommandData *data = getItem();
451 
452   if (level == eDescriptionLevelBrief) {
453     s->Printf(", commands = %s",
454               (data && data->user_source.GetSize() > 0) ? "yes" : "no");
455     return;
456   }
457 
458   s->IndentMore();
459   s->Indent("Breakpoint commands:\n");
460 
461   s->IndentMore();
462   if (data && data->user_source.GetSize() > 0) {
463     const size_t num_strings = data->user_source.GetSize();
464     for (size_t i = 0; i < num_strings; ++i) {
465       s->Indent(data->user_source.GetStringAtIndex(i));
466       s->EOL();
467     }
468   } else {
469     s->PutCString("No commands.\n");
470   }
471   s->IndentLess();
472   s->IndentLess();
473 }
474 
475 void BreakpointOptions::SetCommandDataCallback(
476     std::unique_ptr<CommandData> &cmd_data) {
477   auto baton_sp = std::make_shared<CommandBaton>(std::move(cmd_data));
478   SetCallback(BreakpointOptions::BreakpointOptionsCallbackFunction, baton_sp);
479 }
480 
481 bool BreakpointOptions::BreakpointOptionsCallbackFunction(
482     void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id,
483     lldb::user_id_t break_loc_id) {
484   bool ret_value = true;
485   if (baton == nullptr)
486     return true;
487 
488   CommandData *data = (CommandData *)baton;
489   StringList &commands = data->user_source;
490 
491   if (commands.GetSize() > 0) {
492     ExecutionContext exe_ctx(context->exe_ctx_ref);
493     Target *target = exe_ctx.GetTargetPtr();
494     if (target) {
495       CommandReturnObject result;
496       Debugger &debugger = target->GetDebugger();
497       // Rig up the results secondary output stream to the debugger's, so the
498       // output will come out synchronously
499       // if the debugger is set up that way.
500 
501       StreamSP output_stream(debugger.GetAsyncOutputStream());
502       StreamSP error_stream(debugger.GetAsyncErrorStream());
503       result.SetImmediateOutputStream(output_stream);
504       result.SetImmediateErrorStream(error_stream);
505 
506       CommandInterpreterRunOptions options;
507       options.SetStopOnContinue(true);
508       options.SetStopOnError(data->stop_on_error);
509       options.SetEchoCommands(true);
510       options.SetPrintResults(true);
511       options.SetAddToHistory(false);
512 
513       debugger.GetCommandInterpreter().HandleCommands(commands, &exe_ctx,
514                                                       options, result);
515       result.GetImmediateOutputStream()->Flush();
516       result.GetImmediateErrorStream()->Flush();
517     }
518   }
519   return ret_value;
520 }
521