1 //===-- BreakpointOptions.cpp -----------------------------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 // C Includes 11 // C++ Includes 12 // Other libraries and framework includes 13 // Project includes 14 #include "lldb/Breakpoint/BreakpointOptions.h" 15 16 #include "lldb/Breakpoint/StoppointCallbackContext.h" 17 #include "lldb/Core/Stream.h" 18 #include "lldb/Core/StringList.h" 19 #include "lldb/Core/Value.h" 20 #include "lldb/Interpreter/CommandInterpreter.h" 21 #include "lldb/Interpreter/CommandReturnObject.h" 22 #include "lldb/Target/Process.h" 23 #include "lldb/Target/Target.h" 24 #include "lldb/Target/ThreadSpec.h" 25 26 #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, Error &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 std::string 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.SetErrorStringWithFormat("Unknown breakpoint command language: %s.", 90 interpreter_str.c_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 std::string 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", "EnabledState", "OneShotState"}; 118 119 bool BreakpointOptions::NullCallback(void *baton, 120 StoppointCallbackContext *context, 121 lldb::user_id_t break_id, 122 lldb::user_id_t break_loc_id) { 123 return true; 124 } 125 126 //---------------------------------------------------------------------- 127 // BreakpointOptions constructor 128 //---------------------------------------------------------------------- 129 BreakpointOptions::BreakpointOptions() 130 : m_callback(BreakpointOptions::NullCallback), m_callback_baton_sp(), 131 m_baton_is_command_baton(false), m_callback_is_synchronous(false), 132 m_enabled(true), m_one_shot(false), m_ignore_count(0), m_thread_spec_ap(), 133 m_condition_text(), m_condition_text_hash(0) {} 134 135 BreakpointOptions::BreakpointOptions(const char *condition, bool enabled, 136 int32_t ignore, bool one_shot) 137 : m_callback(nullptr), m_baton_is_command_baton(false), 138 m_callback_is_synchronous(false), m_enabled(enabled), 139 m_one_shot(one_shot), m_ignore_count(ignore), m_condition_text(condition), 140 m_condition_text_hash(0) 141 142 {} 143 144 //---------------------------------------------------------------------- 145 // BreakpointOptions copy constructor 146 //---------------------------------------------------------------------- 147 BreakpointOptions::BreakpointOptions(const BreakpointOptions &rhs) 148 : m_callback(rhs.m_callback), m_callback_baton_sp(rhs.m_callback_baton_sp), 149 m_baton_is_command_baton(rhs.m_baton_is_command_baton), 150 m_callback_is_synchronous(rhs.m_callback_is_synchronous), 151 m_enabled(rhs.m_enabled), m_one_shot(rhs.m_one_shot), 152 m_ignore_count(rhs.m_ignore_count), m_thread_spec_ap() { 153 if (rhs.m_thread_spec_ap.get() != nullptr) 154 m_thread_spec_ap.reset(new ThreadSpec(*rhs.m_thread_spec_ap.get())); 155 m_condition_text = rhs.m_condition_text; 156 m_condition_text_hash = rhs.m_condition_text_hash; 157 } 158 159 //---------------------------------------------------------------------- 160 // BreakpointOptions assignment operator 161 //---------------------------------------------------------------------- 162 const BreakpointOptions &BreakpointOptions:: 163 operator=(const BreakpointOptions &rhs) { 164 m_callback = rhs.m_callback; 165 m_callback_baton_sp = rhs.m_callback_baton_sp; 166 m_baton_is_command_baton = rhs.m_baton_is_command_baton; 167 m_callback_is_synchronous = rhs.m_callback_is_synchronous; 168 m_enabled = rhs.m_enabled; 169 m_one_shot = rhs.m_one_shot; 170 m_ignore_count = rhs.m_ignore_count; 171 if (rhs.m_thread_spec_ap.get() != nullptr) 172 m_thread_spec_ap.reset(new ThreadSpec(*rhs.m_thread_spec_ap.get())); 173 m_condition_text = rhs.m_condition_text; 174 m_condition_text_hash = rhs.m_condition_text_hash; 175 return *this; 176 } 177 178 BreakpointOptions * 179 BreakpointOptions::CopyOptionsNoCallback(BreakpointOptions &orig) { 180 BreakpointHitCallback orig_callback = orig.m_callback; 181 lldb::BatonSP orig_callback_baton_sp = orig.m_callback_baton_sp; 182 bool orig_is_sync = orig.m_callback_is_synchronous; 183 184 orig.ClearCallback(); 185 BreakpointOptions *ret_val = new BreakpointOptions(orig); 186 187 orig.SetCallback(orig_callback, orig_callback_baton_sp, orig_is_sync); 188 189 return ret_val; 190 } 191 192 //---------------------------------------------------------------------- 193 // Destructor 194 //---------------------------------------------------------------------- 195 BreakpointOptions::~BreakpointOptions() = default; 196 197 std::unique_ptr<BreakpointOptions> BreakpointOptions::CreateFromStructuredData( 198 Target &target, const StructuredData::Dictionary &options_dict, 199 Error &error) { 200 bool enabled = true; 201 bool one_shot = false; 202 int32_t ignore_count = 0; 203 std::string condition_text; 204 205 bool success = options_dict.GetValueForKeyAsBoolean( 206 GetKey(OptionNames::EnabledState), enabled); 207 if (!success) { 208 error.SetErrorStringWithFormat("%s key is not a boolean.", 209 GetKey(OptionNames::EnabledState)); 210 return nullptr; 211 } 212 213 success = options_dict.GetValueForKeyAsBoolean( 214 GetKey(OptionNames::OneShotState), one_shot); 215 if (!success) { 216 error.SetErrorStringWithFormat("%s key is not a boolean.", 217 GetKey(OptionNames::OneShotState)); 218 return nullptr; 219 } 220 success = options_dict.GetValueForKeyAsInteger( 221 GetKey(OptionNames::IgnoreCount), ignore_count); 222 if (!success) { 223 error.SetErrorStringWithFormat("%s key is not an integer.", 224 GetKey(OptionNames::IgnoreCount)); 225 return nullptr; 226 } 227 228 std::unique_ptr<CommandData> cmd_data_up; 229 StructuredData::Dictionary *cmds_dict; 230 success = options_dict.GetValueForKeyAsDictionary( 231 CommandData::GetSerializationKey(), cmds_dict); 232 if (success && cmds_dict) { 233 Error cmds_error; 234 cmd_data_up = CommandData::CreateFromStructuredData(*cmds_dict, cmds_error); 235 if (cmds_error.Fail()) { 236 error.SetErrorStringWithFormat( 237 "Failed to deserialize breakpoint command options: %s.", 238 cmds_error.AsCString()); 239 return nullptr; 240 } 241 } 242 243 auto bp_options = llvm::make_unique<BreakpointOptions>( 244 condition_text.c_str(), enabled, ignore_count, one_shot); 245 if (cmd_data_up.get()) { 246 if (cmd_data_up->interpreter == eScriptLanguageNone) 247 bp_options->SetCommandDataCallback(cmd_data_up); 248 else { 249 ScriptInterpreter *interp = 250 target.GetDebugger().GetCommandInterpreter().GetScriptInterpreter(); 251 if (!interp) { 252 error.SetErrorStringWithFormat( 253 "Can't set script commands - no script interpreter"); 254 return nullptr; 255 } 256 if (interp->GetLanguage() != cmd_data_up->interpreter) { 257 error.SetErrorStringWithFormat( 258 "Current script language doesn't match breakpoint's language: %s", 259 ScriptInterpreter::LanguageToString(cmd_data_up->interpreter) 260 .c_str()); 261 return nullptr; 262 } 263 Error script_error; 264 script_error = 265 interp->SetBreakpointCommandCallback(bp_options.get(), cmd_data_up); 266 if (script_error.Fail()) { 267 error.SetErrorStringWithFormat("Error generating script callback: %s.", 268 error.AsCString()); 269 return nullptr; 270 } 271 } 272 } 273 274 StructuredData::Dictionary *thread_spec_dict; 275 success = options_dict.GetValueForKeyAsDictionary( 276 ThreadSpec::GetSerializationKey(), thread_spec_dict); 277 if (success) { 278 Error thread_spec_error; 279 std::unique_ptr<ThreadSpec> thread_spec_up = 280 ThreadSpec::CreateFromStructuredData(*thread_spec_dict, 281 thread_spec_error); 282 if (thread_spec_error.Fail()) { 283 error.SetErrorStringWithFormat( 284 "Failed to deserialize breakpoint thread spec options: %s.", 285 thread_spec_error.AsCString()); 286 return nullptr; 287 } 288 bp_options->SetThreadSpec(thread_spec_up); 289 } 290 return bp_options; 291 } 292 293 StructuredData::ObjectSP BreakpointOptions::SerializeToStructuredData() { 294 StructuredData::DictionarySP options_dict_sp( 295 new StructuredData::Dictionary()); 296 options_dict_sp->AddBooleanItem(GetKey(OptionNames::EnabledState), m_enabled); 297 options_dict_sp->AddBooleanItem(GetKey(OptionNames::OneShotState), 298 m_one_shot); 299 options_dict_sp->AddIntegerItem(GetKey(OptionNames::IgnoreCount), 300 m_ignore_count); 301 options_dict_sp->AddStringItem(GetKey(OptionNames::ConditionText), 302 m_condition_text); 303 if (m_baton_is_command_baton) { 304 auto cmd_baton = 305 std::static_pointer_cast<CommandBaton>(m_callback_baton_sp); 306 StructuredData::ObjectSP commands_sp = 307 cmd_baton->getItem()->SerializeToStructuredData(); 308 if (commands_sp) { 309 options_dict_sp->AddItem( 310 BreakpointOptions::CommandData::GetSerializationKey(), commands_sp); 311 } 312 } 313 if (m_thread_spec_ap) { 314 StructuredData::ObjectSP thread_spec_sp = 315 m_thread_spec_ap->SerializeToStructuredData(); 316 options_dict_sp->AddItem(ThreadSpec::GetSerializationKey(), thread_spec_sp); 317 } 318 319 return options_dict_sp; 320 } 321 322 //------------------------------------------------------------------ 323 // Callbacks 324 //------------------------------------------------------------------ 325 void BreakpointOptions::SetCallback(BreakpointHitCallback callback, 326 const lldb::BatonSP &callback_baton_sp, 327 bool callback_is_synchronous) { 328 // FIXME: This seems unsafe. If BatonSP actually *is* a CommandBaton, but 329 // in a shared_ptr<Baton> instead of a shared_ptr<CommandBaton>, then we 330 // will set m_baton_is_command_baton to false, which is incorrect. 331 // One possible solution is to make the base Baton class provide a method 332 // such as: 333 // virtual StringRef getBatonId() const { return ""; } 334 // and have CommandBaton override this to return something unique, and then 335 // check for it here. Another option might be to make Baton using the llvm 336 // casting infrastructure, so that we could write something like: 337 // if (llvm::isa<CommandBaton>(callback_baton_sp)) 338 // at relevant callsites instead of storing a boolean. 339 m_callback_is_synchronous = callback_is_synchronous; 340 m_callback = callback; 341 m_callback_baton_sp = callback_baton_sp; 342 m_baton_is_command_baton = false; 343 } 344 345 void BreakpointOptions::SetCallback( 346 BreakpointHitCallback callback, 347 const BreakpointOptions::CommandBatonSP &callback_baton_sp, 348 bool callback_is_synchronous) { 349 m_callback_is_synchronous = callback_is_synchronous; 350 m_callback = callback; 351 m_callback_baton_sp = callback_baton_sp; 352 m_baton_is_command_baton = true; 353 } 354 355 void BreakpointOptions::ClearCallback() { 356 m_callback = BreakpointOptions::NullCallback; 357 m_callback_is_synchronous = false; 358 m_callback_baton_sp.reset(); 359 m_baton_is_command_baton = false; 360 } 361 362 Baton *BreakpointOptions::GetBaton() { return m_callback_baton_sp.get(); } 363 364 const Baton *BreakpointOptions::GetBaton() const { 365 return m_callback_baton_sp.get(); 366 } 367 368 bool BreakpointOptions::InvokeCallback(StoppointCallbackContext *context, 369 lldb::user_id_t break_id, 370 lldb::user_id_t break_loc_id) { 371 if (m_callback && context->is_synchronous == IsCallbackSynchronous()) { 372 return m_callback(m_callback_baton_sp ? m_callback_baton_sp->data() 373 : nullptr, 374 context, break_id, break_loc_id); 375 } else 376 return true; 377 } 378 379 bool BreakpointOptions::HasCallback() const { 380 return m_callback != BreakpointOptions::NullCallback; 381 } 382 383 bool BreakpointOptions::GetCommandLineCallbacks(StringList &command_list) { 384 if (!HasCallback()) 385 return false; 386 if (!m_baton_is_command_baton) 387 return false; 388 389 auto cmd_baton = std::static_pointer_cast<CommandBaton>(m_callback_baton_sp); 390 CommandData *data = cmd_baton->getItem(); 391 if (!data) 392 return false; 393 command_list = data->user_source; 394 return true; 395 } 396 397 void BreakpointOptions::SetCondition(const char *condition) { 398 if (!condition) 399 condition = ""; 400 401 m_condition_text.assign(condition); 402 std::hash<std::string> hasher; 403 m_condition_text_hash = hasher(m_condition_text); 404 } 405 406 const char *BreakpointOptions::GetConditionText(size_t *hash) const { 407 if (!m_condition_text.empty()) { 408 if (hash) 409 *hash = m_condition_text_hash; 410 411 return m_condition_text.c_str(); 412 } else { 413 return nullptr; 414 } 415 } 416 417 const ThreadSpec *BreakpointOptions::GetThreadSpecNoCreate() const { 418 return m_thread_spec_ap.get(); 419 } 420 421 ThreadSpec *BreakpointOptions::GetThreadSpec() { 422 if (m_thread_spec_ap.get() == nullptr) 423 m_thread_spec_ap.reset(new ThreadSpec()); 424 425 return m_thread_spec_ap.get(); 426 } 427 428 void BreakpointOptions::SetThreadID(lldb::tid_t thread_id) { 429 GetThreadSpec()->SetTID(thread_id); 430 } 431 432 void BreakpointOptions::SetThreadSpec( 433 std::unique_ptr<ThreadSpec> &thread_spec_up) { 434 m_thread_spec_ap = std::move(thread_spec_up); 435 } 436 437 void BreakpointOptions::GetDescription(Stream *s, 438 lldb::DescriptionLevel level) const { 439 // Figure out if there are any options not at their default value, and only 440 // print 441 // anything if there are: 442 443 if (m_ignore_count != 0 || !m_enabled || m_one_shot || 444 (GetThreadSpecNoCreate() != nullptr && 445 GetThreadSpecNoCreate()->HasSpecification())) { 446 if (level == lldb::eDescriptionLevelVerbose) { 447 s->EOL(); 448 s->IndentMore(); 449 s->Indent(); 450 s->PutCString("Breakpoint Options:\n"); 451 s->IndentMore(); 452 s->Indent(); 453 } else 454 s->PutCString(" Options: "); 455 456 if (m_ignore_count > 0) 457 s->Printf("ignore: %d ", m_ignore_count); 458 s->Printf("%sabled ", m_enabled ? "en" : "dis"); 459 460 if (m_one_shot) 461 s->Printf("one-shot "); 462 463 if (m_thread_spec_ap.get()) 464 m_thread_spec_ap->GetDescription(s, level); 465 466 if (level == lldb::eDescriptionLevelFull) { 467 s->IndentLess(); 468 s->IndentMore(); 469 } 470 } 471 472 if (m_callback_baton_sp.get()) { 473 if (level != eDescriptionLevelBrief) { 474 s->EOL(); 475 m_callback_baton_sp->GetDescription(s, level); 476 } 477 } 478 if (!m_condition_text.empty()) { 479 if (level != eDescriptionLevelBrief) { 480 s->EOL(); 481 s->Printf("Condition: %s\n", m_condition_text.c_str()); 482 } 483 } 484 } 485 486 void BreakpointOptions::CommandBaton::GetDescription( 487 Stream *s, lldb::DescriptionLevel level) const { 488 const CommandData *data = getItem(); 489 490 if (level == eDescriptionLevelBrief) { 491 s->Printf(", commands = %s", 492 (data && data->user_source.GetSize() > 0) ? "yes" : "no"); 493 return; 494 } 495 496 s->IndentMore(); 497 s->Indent("Breakpoint commands"); 498 if (data->interpreter != eScriptLanguageNone) 499 s->Printf(" (%s):\n", 500 ScriptInterpreter::LanguageToString(data->interpreter).c_str()); 501 else 502 s->PutCString(":\n"); 503 504 s->IndentMore(); 505 if (data && data->user_source.GetSize() > 0) { 506 const size_t num_strings = data->user_source.GetSize(); 507 for (size_t i = 0; i < num_strings; ++i) { 508 s->Indent(data->user_source.GetStringAtIndex(i)); 509 s->EOL(); 510 } 511 } else { 512 s->PutCString("No commands.\n"); 513 } 514 s->IndentLess(); 515 s->IndentLess(); 516 } 517 518 void BreakpointOptions::SetCommandDataCallback( 519 std::unique_ptr<CommandData> &cmd_data) { 520 cmd_data->interpreter = eScriptLanguageNone; 521 auto baton_sp = std::make_shared<CommandBaton>(std::move(cmd_data)); 522 SetCallback(BreakpointOptions::BreakpointOptionsCallbackFunction, baton_sp); 523 } 524 525 bool BreakpointOptions::BreakpointOptionsCallbackFunction( 526 void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id, 527 lldb::user_id_t break_loc_id) { 528 bool ret_value = true; 529 if (baton == nullptr) 530 return true; 531 532 CommandData *data = (CommandData *)baton; 533 StringList &commands = data->user_source; 534 535 if (commands.GetSize() > 0) { 536 ExecutionContext exe_ctx(context->exe_ctx_ref); 537 Target *target = exe_ctx.GetTargetPtr(); 538 if (target) { 539 CommandReturnObject result; 540 Debugger &debugger = target->GetDebugger(); 541 // Rig up the results secondary output stream to the debugger's, so the 542 // output will come out synchronously 543 // if the debugger is set up that way. 544 545 StreamSP output_stream(debugger.GetAsyncOutputStream()); 546 StreamSP error_stream(debugger.GetAsyncErrorStream()); 547 result.SetImmediateOutputStream(output_stream); 548 result.SetImmediateErrorStream(error_stream); 549 550 CommandInterpreterRunOptions options; 551 options.SetStopOnContinue(true); 552 options.SetStopOnError(data->stop_on_error); 553 options.SetEchoCommands(true); 554 options.SetPrintResults(true); 555 options.SetAddToHistory(false); 556 557 debugger.GetCommandInterpreter().HandleCommands(commands, &exe_ctx, 558 options, result); 559 result.GetImmediateOutputStream()->Flush(); 560 result.GetImmediateErrorStream()->Flush(); 561 } 562 } 563 return ret_value; 564 } 565