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/Value.h"
18 #include "lldb/Interpreter/CommandInterpreter.h"
19 #include "lldb/Interpreter/CommandReturnObject.h"
20 #include "lldb/Target/Process.h"
21 #include "lldb/Target/Target.h"
22 #include "lldb/Target/ThreadSpec.h"
23 #include "lldb/Utility/Stream.h"
24 #include "lldb/Utility/StringList.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   options_dict_sp->AddStringItem(
59       GetKey(OptionNames::Interpreter),
60       ScriptInterpreter::LanguageToString(interpreter));
61   return options_dict_sp;
62 }
63 
64 std::unique_ptr<BreakpointOptions::CommandData>
65 BreakpointOptions::CommandData::CreateFromStructuredData(
66     const StructuredData::Dictionary &options_dict, Status &error) {
67   std::unique_ptr<CommandData> data_up(new CommandData());
68   bool found_something = false;
69 
70   bool success = options_dict.GetValueForKeyAsBoolean(
71       GetKey(OptionNames::StopOnError), data_up->stop_on_error);
72 
73   if (success)
74     found_something = true;
75 
76   llvm::StringRef interpreter_str;
77   ScriptLanguage interp_language;
78   success = options_dict.GetValueForKeyAsString(
79       GetKey(OptionNames::Interpreter), interpreter_str);
80 
81   if (!success) {
82     error.SetErrorString("Missing command language value.");
83     return data_up;
84   }
85 
86   found_something = true;
87   interp_language = ScriptInterpreter::StringToLanguage(interpreter_str);
88   if (interp_language == eScriptLanguageUnknown) {
89     error.SetErrorStringWithFormatv("Unknown breakpoint command language: {0}.",
90                                     interpreter_str);
91     return data_up;
92   }
93   data_up->interpreter = interp_language;
94 
95   StructuredData::Array *user_source;
96   success = options_dict.GetValueForKeyAsArray(GetKey(OptionNames::UserSource),
97                                                user_source);
98   if (success) {
99     found_something = true;
100     size_t num_elems = user_source->GetSize();
101     for (size_t i = 0; i < num_elems; i++) {
102       llvm::StringRef elem_string;
103       success = user_source->GetItemAtIndexAsString(i, elem_string);
104       if (success)
105         data_up->user_source.AppendString(elem_string);
106     }
107   }
108 
109   if (found_something)
110     return data_up;
111   else
112     return std::unique_ptr<BreakpointOptions::CommandData>();
113 }
114 
115 const char *BreakpointOptions::g_option_names[(
116     size_t)BreakpointOptions::OptionNames::LastOptionName]{
117     "ConditionText", "IgnoreCount",
118     "EnabledState", "OneShotState", "AutoContinue"};
119 
120 bool BreakpointOptions::NullCallback(void *baton,
121                                      StoppointCallbackContext *context,
122                                      lldb::user_id_t break_id,
123                                      lldb::user_id_t break_loc_id) {
124   return true;
125 }
126 
127 //----------------------------------------------------------------------
128 // BreakpointOptions constructor
129 //----------------------------------------------------------------------
130 BreakpointOptions::BreakpointOptions(bool all_flags_set)
131     : m_callback(BreakpointOptions::NullCallback), m_callback_baton_sp(),
132       m_baton_is_command_baton(false), m_callback_is_synchronous(false),
133       m_enabled(true), m_one_shot(false), m_ignore_count(0), m_thread_spec_ap(),
134       m_condition_text(), m_condition_text_hash(0), m_auto_continue(false),
135       m_set_flags() {
136         if (all_flags_set)
137           m_set_flags.Set(~((Flags::ValueType) 0));
138       }
139 
140 BreakpointOptions::BreakpointOptions(const char *condition, bool enabled,
141                                      int32_t ignore, bool one_shot,
142                                      bool auto_continue)
143     : m_callback(nullptr), m_baton_is_command_baton(false),
144       m_callback_is_synchronous(false), m_enabled(enabled),
145       m_one_shot(one_shot), m_ignore_count(ignore), m_condition_text(condition),
146       m_condition_text_hash(0), m_auto_continue(auto_continue)
147 {
148     m_set_flags.Set(eEnabled | eIgnoreCount | eOneShot
149                    | eCondition | eAutoContinue);
150 }
151 
152 //----------------------------------------------------------------------
153 // BreakpointOptions copy constructor
154 //----------------------------------------------------------------------
155 BreakpointOptions::BreakpointOptions(const BreakpointOptions &rhs)
156     : m_callback(rhs.m_callback), m_callback_baton_sp(rhs.m_callback_baton_sp),
157       m_baton_is_command_baton(rhs.m_baton_is_command_baton),
158       m_callback_is_synchronous(rhs.m_callback_is_synchronous),
159       m_enabled(rhs.m_enabled), m_one_shot(rhs.m_one_shot),
160       m_ignore_count(rhs.m_ignore_count), m_thread_spec_ap(),
161       m_auto_continue(rhs.m_auto_continue),
162       m_set_flags(rhs.m_set_flags) {
163   if (rhs.m_thread_spec_ap.get() != nullptr)
164     m_thread_spec_ap.reset(new ThreadSpec(*rhs.m_thread_spec_ap.get()));
165   m_condition_text = rhs.m_condition_text;
166   m_condition_text_hash = rhs.m_condition_text_hash;
167 }
168 
169 //----------------------------------------------------------------------
170 // BreakpointOptions assignment operator
171 //----------------------------------------------------------------------
172 const BreakpointOptions &BreakpointOptions::
173 operator=(const BreakpointOptions &rhs) {
174   m_callback = rhs.m_callback;
175   m_callback_baton_sp = rhs.m_callback_baton_sp;
176   m_baton_is_command_baton = rhs.m_baton_is_command_baton;
177   m_callback_is_synchronous = rhs.m_callback_is_synchronous;
178   m_enabled = rhs.m_enabled;
179   m_one_shot = rhs.m_one_shot;
180   m_ignore_count = rhs.m_ignore_count;
181   if (rhs.m_thread_spec_ap.get() != nullptr)
182     m_thread_spec_ap.reset(new ThreadSpec(*rhs.m_thread_spec_ap.get()));
183   m_condition_text = rhs.m_condition_text;
184   m_condition_text_hash = rhs.m_condition_text_hash;
185   m_auto_continue = rhs.m_auto_continue;
186   m_set_flags = rhs.m_set_flags;
187   return *this;
188 }
189 
190 //----------------------------------------------------------------------
191 // Destructor
192 //----------------------------------------------------------------------
193 BreakpointOptions::~BreakpointOptions() = default;
194 
195 std::unique_ptr<BreakpointOptions> BreakpointOptions::CreateFromStructuredData(
196     Target &target, const StructuredData::Dictionary &options_dict,
197     Status &error) {
198   bool enabled = true;
199   bool one_shot = false;
200   bool auto_continue = false;
201   int32_t ignore_count = 0;
202   llvm::StringRef condition_ref("");
203   Flags set_options;
204 
205   const char *key = GetKey(OptionNames::EnabledState);
206   bool success;
207   if (key) {
208     success = options_dict.GetValueForKeyAsBoolean(key, enabled);
209     if (!success) {
210       error.SetErrorStringWithFormat("%s key is not a boolean.",
211                                    GetKey(OptionNames::EnabledState));
212       return nullptr;
213     }
214     set_options.Set(eEnabled);
215   }
216 
217   key = GetKey(OptionNames::OneShotState);
218   if (key) {
219     success = options_dict.GetValueForKeyAsBoolean(key, one_shot);
220     if (!success) {
221       error.SetErrorStringWithFormat("%s key is not a boolean.",
222                                      GetKey(OptionNames::OneShotState));
223       return nullptr;
224       }
225       set_options.Set(eOneShot);
226   }
227 
228   key = GetKey(OptionNames::AutoContinue);
229   if (key) {
230     success = options_dict.GetValueForKeyAsBoolean(key, auto_continue);
231     if (!success) {
232       error.SetErrorStringWithFormat("%s key is not a boolean.",
233                                      GetKey(OptionNames::AutoContinue));
234       return nullptr;
235       }
236       set_options.Set(eAutoContinue);
237   }
238 
239   key = GetKey(OptionNames::IgnoreCount);
240   if (key) {
241     success = options_dict.GetValueForKeyAsInteger(key, ignore_count);
242     if (!success) {
243       error.SetErrorStringWithFormat("%s key is not an integer.",
244                                      GetKey(OptionNames::IgnoreCount));
245       return nullptr;
246     }
247     set_options.Set(eIgnoreCount);
248   }
249 
250   key = GetKey(OptionNames::ConditionText);
251   if (key) {
252     success = options_dict.GetValueForKeyAsString(key, condition_ref);
253     if (!success) {
254       error.SetErrorStringWithFormat("%s key is not an string.",
255                                      GetKey(OptionNames::ConditionText));
256       return nullptr;
257     }
258     set_options.Set(eCondition);
259   }
260 
261   std::unique_ptr<CommandData> cmd_data_up;
262   StructuredData::Dictionary *cmds_dict;
263   success = options_dict.GetValueForKeyAsDictionary(
264       CommandData::GetSerializationKey(), cmds_dict);
265   if (success && cmds_dict) {
266     Status cmds_error;
267     cmd_data_up = CommandData::CreateFromStructuredData(*cmds_dict, cmds_error);
268     if (cmds_error.Fail()) {
269       error.SetErrorStringWithFormat(
270           "Failed to deserialize breakpoint command options: %s.",
271           cmds_error.AsCString());
272       return nullptr;
273     }
274   }
275 
276   auto bp_options = llvm::make_unique<BreakpointOptions>(
277       condition_ref.str().c_str(), enabled,
278       ignore_count, one_shot, auto_continue);
279   if (cmd_data_up.get()) {
280     if (cmd_data_up->interpreter == eScriptLanguageNone)
281       bp_options->SetCommandDataCallback(cmd_data_up);
282     else {
283       ScriptInterpreter *interp =
284           target.GetDebugger().GetCommandInterpreter().GetScriptInterpreter();
285       if (!interp) {
286         error.SetErrorStringWithFormat(
287             "Can't set script commands - no script interpreter");
288         return nullptr;
289       }
290       if (interp->GetLanguage() != cmd_data_up->interpreter) {
291         error.SetErrorStringWithFormat(
292             "Current script language doesn't match breakpoint's language: %s",
293             ScriptInterpreter::LanguageToString(cmd_data_up->interpreter)
294                 .c_str());
295         return nullptr;
296       }
297       Status script_error;
298       script_error =
299           interp->SetBreakpointCommandCallback(bp_options.get(), cmd_data_up);
300       if (script_error.Fail()) {
301         error.SetErrorStringWithFormat("Error generating script callback: %s.",
302                                        error.AsCString());
303         return nullptr;
304       }
305     }
306   }
307 
308   StructuredData::Dictionary *thread_spec_dict;
309   success = options_dict.GetValueForKeyAsDictionary(
310       ThreadSpec::GetSerializationKey(), thread_spec_dict);
311   if (success) {
312     Status thread_spec_error;
313     std::unique_ptr<ThreadSpec> thread_spec_up =
314         ThreadSpec::CreateFromStructuredData(*thread_spec_dict,
315                                              thread_spec_error);
316     if (thread_spec_error.Fail()) {
317       error.SetErrorStringWithFormat(
318           "Failed to deserialize breakpoint thread spec options: %s.",
319           thread_spec_error.AsCString());
320       return nullptr;
321     }
322     bp_options->SetThreadSpec(thread_spec_up);
323   }
324   return bp_options;
325 }
326 
327 StructuredData::ObjectSP BreakpointOptions::SerializeToStructuredData() {
328   StructuredData::DictionarySP options_dict_sp(
329       new StructuredData::Dictionary());
330   if (m_set_flags.Set(eEnabled))
331     options_dict_sp->AddBooleanItem(GetKey(OptionNames::EnabledState),
332                                     m_enabled);
333   if (m_set_flags.Set(eOneShot))
334     options_dict_sp->AddBooleanItem(GetKey(OptionNames::OneShotState),
335                                m_one_shot);
336   if (m_set_flags.Set(eAutoContinue))
337     options_dict_sp->AddBooleanItem(GetKey(OptionNames::AutoContinue),
338                                m_auto_continue);
339   if (m_set_flags.Set(eIgnoreCount))
340     options_dict_sp->AddIntegerItem(GetKey(OptionNames::IgnoreCount),
341                                     m_ignore_count);
342   if (m_set_flags.Set(eCondition))
343     options_dict_sp->AddStringItem(GetKey(OptionNames::ConditionText),
344                                    m_condition_text);
345 
346   if (m_set_flags.Set(eCallback) && m_baton_is_command_baton) {
347     auto cmd_baton =
348         std::static_pointer_cast<CommandBaton>(m_callback_baton_sp);
349     StructuredData::ObjectSP commands_sp =
350         cmd_baton->getItem()->SerializeToStructuredData();
351     if (commands_sp) {
352       options_dict_sp->AddItem(
353           BreakpointOptions::CommandData::GetSerializationKey(), commands_sp);
354     }
355   }
356   if (m_set_flags.Set(eThreadSpec) && m_thread_spec_ap) {
357     StructuredData::ObjectSP thread_spec_sp =
358         m_thread_spec_ap->SerializeToStructuredData();
359     options_dict_sp->AddItem(ThreadSpec::GetSerializationKey(), thread_spec_sp);
360   }
361 
362   return options_dict_sp;
363 }
364 
365 //------------------------------------------------------------------
366 // Callbacks
367 //------------------------------------------------------------------
368 void BreakpointOptions::SetCallback(BreakpointHitCallback callback,
369                                     const lldb::BatonSP &callback_baton_sp,
370                                     bool callback_is_synchronous) {
371   // FIXME: This seems unsafe.  If BatonSP actually *is* a CommandBaton, but
372   // in a shared_ptr<Baton> instead of a shared_ptr<CommandBaton>, then we
373   // will set m_baton_is_command_baton to false, which is incorrect.
374   // One possible solution is to make the base Baton class provide a method
375   // such as:
376   //     virtual StringRef getBatonId() const { return ""; }
377   // and have CommandBaton override this to return something unique, and then
378   // check for it here.  Another option might be to make Baton using the llvm
379   // casting infrastructure, so that we could write something like:
380   //     if (llvm::isa<CommandBaton>(callback_baton_sp))
381   // at relevant callsites instead of storing a boolean.
382   m_callback_is_synchronous = callback_is_synchronous;
383   m_callback = callback;
384   m_callback_baton_sp = callback_baton_sp;
385   m_baton_is_command_baton = false;
386   m_set_flags.Set(eCallback);
387 }
388 
389 void BreakpointOptions::SetCallback(
390     BreakpointHitCallback callback,
391     const BreakpointOptions::CommandBatonSP &callback_baton_sp,
392     bool callback_is_synchronous) {
393   m_callback_is_synchronous = callback_is_synchronous;
394   m_callback = callback;
395   m_callback_baton_sp = callback_baton_sp;
396   m_baton_is_command_baton = true;
397   m_set_flags.Set(eCallback);
398 }
399 
400 void BreakpointOptions::ClearCallback() {
401   m_callback = BreakpointOptions::NullCallback;
402   m_callback_is_synchronous = false;
403   m_callback_baton_sp.reset();
404   m_baton_is_command_baton = false;
405   m_set_flags.Clear(eCallback);
406 }
407 
408 Baton *BreakpointOptions::GetBaton() { return m_callback_baton_sp.get(); }
409 
410 const Baton *BreakpointOptions::GetBaton() const {
411   return m_callback_baton_sp.get();
412 }
413 
414 bool BreakpointOptions::InvokeCallback(StoppointCallbackContext *context,
415                                        lldb::user_id_t break_id,
416                                        lldb::user_id_t break_loc_id) {
417   if (m_callback && context->is_synchronous == IsCallbackSynchronous()) {
418     return m_callback(m_callback_baton_sp ? m_callback_baton_sp->data()
419                                           : nullptr,
420                       context, break_id, break_loc_id);
421   } else
422     return true;
423 }
424 
425 bool BreakpointOptions::HasCallback() const {
426   return m_callback != BreakpointOptions::NullCallback;
427 }
428 
429 bool BreakpointOptions::GetCommandLineCallbacks(StringList &command_list) {
430   if (!HasCallback())
431     return false;
432   if (!m_baton_is_command_baton)
433     return false;
434 
435   auto cmd_baton = std::static_pointer_cast<CommandBaton>(m_callback_baton_sp);
436   CommandData *data = cmd_baton->getItem();
437   if (!data)
438     return false;
439   command_list = data->user_source;
440   return true;
441 }
442 
443 void BreakpointOptions::SetCondition(const char *condition) {
444   if (!condition || condition[0] == '\0') {
445     condition = "";
446     m_set_flags.Clear(eCondition);
447   }
448   else
449     m_set_flags.Set(eCondition);
450 
451   m_condition_text.assign(condition);
452   std::hash<std::string> hasher;
453   m_condition_text_hash = hasher(m_condition_text);
454 }
455 
456 const char *BreakpointOptions::GetConditionText(size_t *hash) const {
457   if (!m_condition_text.empty()) {
458     if (hash)
459       *hash = m_condition_text_hash;
460 
461     return m_condition_text.c_str();
462   } else {
463     return nullptr;
464   }
465 }
466 
467 const ThreadSpec *BreakpointOptions::GetThreadSpecNoCreate() const {
468   return m_thread_spec_ap.get();
469 }
470 
471 ThreadSpec *BreakpointOptions::GetThreadSpec() {
472   if (m_thread_spec_ap.get() == nullptr)
473     m_thread_spec_ap.reset(new ThreadSpec());
474 
475   return m_thread_spec_ap.get();
476 }
477 
478 void BreakpointOptions::SetThreadID(lldb::tid_t thread_id) {
479   GetThreadSpec()->SetTID(thread_id);
480   m_set_flags.Set(eThreadSpec);
481 }
482 
483 void BreakpointOptions::SetThreadSpec(
484     std::unique_ptr<ThreadSpec> &thread_spec_up) {
485   m_thread_spec_ap = std::move(thread_spec_up);
486   m_set_flags.Set(eThreadSpec);
487 }
488 
489 void BreakpointOptions::GetDescription(Stream *s,
490                                        lldb::DescriptionLevel level) const {
491   // Figure out if there are any options not at their default value, and only
492   // print
493   // anything if there are:
494 
495   if (m_ignore_count != 0 || !m_enabled || m_one_shot || m_auto_continue ||
496       (GetThreadSpecNoCreate() != nullptr &&
497        GetThreadSpecNoCreate()->HasSpecification())) {
498     if (level == lldb::eDescriptionLevelVerbose) {
499       s->EOL();
500       s->IndentMore();
501       s->Indent();
502       s->PutCString("Breakpoint Options:\n");
503       s->IndentMore();
504       s->Indent();
505     } else
506       s->PutCString(" Options: ");
507 
508     if (m_ignore_count > 0)
509       s->Printf("ignore: %d ", m_ignore_count);
510     s->Printf("%sabled ", m_enabled ? "en" : "dis");
511 
512     if (m_one_shot)
513       s->Printf("one-shot ");
514 
515     if (m_auto_continue)
516       s->Printf("auto-continue ");
517 
518     if (m_thread_spec_ap.get())
519       m_thread_spec_ap->GetDescription(s, level);
520 
521     if (level == lldb::eDescriptionLevelFull) {
522       s->IndentLess();
523       s->IndentMore();
524     }
525   }
526 
527   if (m_callback_baton_sp.get()) {
528     if (level != eDescriptionLevelBrief) {
529       s->EOL();
530       m_callback_baton_sp->GetDescription(s, level);
531     }
532   }
533   if (!m_condition_text.empty()) {
534     if (level != eDescriptionLevelBrief) {
535       s->EOL();
536       s->Printf("Condition: %s\n", m_condition_text.c_str());
537     }
538   }
539 }
540 
541 void BreakpointOptions::CommandBaton::GetDescription(
542     Stream *s, lldb::DescriptionLevel level) const {
543   const CommandData *data = getItem();
544 
545   if (level == eDescriptionLevelBrief) {
546     s->Printf(", commands = %s",
547               (data && data->user_source.GetSize() > 0) ? "yes" : "no");
548     return;
549   }
550 
551   s->IndentMore();
552   s->Indent("Breakpoint commands");
553   if (data->interpreter != eScriptLanguageNone)
554     s->Printf(" (%s):\n",
555               ScriptInterpreter::LanguageToString(data->interpreter).c_str());
556   else
557     s->PutCString(":\n");
558 
559   s->IndentMore();
560   if (data && data->user_source.GetSize() > 0) {
561     const size_t num_strings = data->user_source.GetSize();
562     for (size_t i = 0; i < num_strings; ++i) {
563       s->Indent(data->user_source.GetStringAtIndex(i));
564       s->EOL();
565     }
566   } else {
567     s->PutCString("No commands.\n");
568   }
569   s->IndentLess();
570   s->IndentLess();
571 }
572 
573 void BreakpointOptions::SetCommandDataCallback(
574     std::unique_ptr<CommandData> &cmd_data) {
575   cmd_data->interpreter = eScriptLanguageNone;
576   auto baton_sp = std::make_shared<CommandBaton>(std::move(cmd_data));
577   SetCallback(BreakpointOptions::BreakpointOptionsCallbackFunction, baton_sp);
578   m_set_flags.Set(eCallback);
579 }
580 
581 bool BreakpointOptions::BreakpointOptionsCallbackFunction(
582     void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id,
583     lldb::user_id_t break_loc_id) {
584   bool ret_value = true;
585   if (baton == nullptr)
586     return true;
587 
588   CommandData *data = (CommandData *)baton;
589   StringList &commands = data->user_source;
590 
591   if (commands.GetSize() > 0) {
592     ExecutionContext exe_ctx(context->exe_ctx_ref);
593     Target *target = exe_ctx.GetTargetPtr();
594     if (target) {
595       CommandReturnObject result;
596       Debugger &debugger = target->GetDebugger();
597       // Rig up the results secondary output stream to the debugger's, so the
598       // output will come out synchronously
599       // if the debugger is set up that way.
600 
601       StreamSP output_stream(debugger.GetAsyncOutputStream());
602       StreamSP error_stream(debugger.GetAsyncErrorStream());
603       result.SetImmediateOutputStream(output_stream);
604       result.SetImmediateErrorStream(error_stream);
605 
606       CommandInterpreterRunOptions options;
607       options.SetStopOnContinue(true);
608       options.SetStopOnError(data->stop_on_error);
609       options.SetEchoCommands(true);
610       options.SetPrintResults(true);
611       options.SetAddToHistory(false);
612 
613       debugger.GetCommandInterpreter().HandleCommands(commands, &exe_ctx,
614                                                       options, result);
615       result.GetImmediateOutputStream()->Flush();
616       result.GetImmediateErrorStream()->Flush();
617     }
618   }
619   return ret_value;
620 }
621