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