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