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