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