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