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