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 if (!script_source.empty()) { 59 StructuredData::StringSP item_sp(new StructuredData::String(script_source)); 60 options_dict_sp->AddItem(GetKey(OptionNames::ScriptSource), user_source_sp); 61 } 62 return options_dict_sp; 63 } 64 65 std::unique_ptr<BreakpointOptions::CommandData> 66 BreakpointOptions::CommandData::CreateFromStructuredData( 67 const StructuredData::Dictionary &options_dict, Error &error) { 68 std::string script_source; 69 std::unique_ptr<CommandData> data_up(new CommandData()); 70 bool found_something = false; 71 72 bool success = options_dict.GetValueForKeyAsBoolean( 73 GetKey(OptionNames::StopOnError), data_up->stop_on_error); 74 75 if (success) 76 found_something = true; 77 78 success = options_dict.GetValueForKeyAsString( 79 GetKey(OptionNames::ScriptSource), data_up->script_source); 80 81 if (success) 82 found_something = true; 83 84 StructuredData::Array *user_source; 85 success = options_dict.GetValueForKeyAsArray(GetKey(OptionNames::UserSource), 86 user_source); 87 if (success) { 88 found_something = true; 89 size_t num_elems = user_source->GetSize(); 90 for (size_t i = 0; i < num_elems; i++) { 91 std::string elem_string; 92 success = user_source->GetItemAtIndexAsString(i, elem_string); 93 if (success) 94 data_up->user_source.AppendString(elem_string); 95 } 96 } 97 98 if (found_something) 99 return data_up; 100 else 101 return std::unique_ptr<BreakpointOptions::CommandData>(); 102 } 103 104 const char *BreakpointOptions::g_option_names[( 105 size_t)BreakpointOptions::OptionNames::LastOptionName]{ 106 "ConditionText", "IgnoreCount", "EnabledState", "OneShotState"}; 107 108 bool BreakpointOptions::NullCallback(void *baton, 109 StoppointCallbackContext *context, 110 lldb::user_id_t break_id, 111 lldb::user_id_t break_loc_id) { 112 return true; 113 } 114 115 //---------------------------------------------------------------------- 116 // BreakpointOptions constructor 117 //---------------------------------------------------------------------- 118 BreakpointOptions::BreakpointOptions() 119 : m_callback(BreakpointOptions::NullCallback), m_callback_baton_sp(), 120 m_baton_is_command_baton(false), m_callback_is_synchronous(false), 121 m_enabled(true), m_one_shot(false), m_ignore_count(0), m_thread_spec_ap(), 122 m_condition_text(), m_condition_text_hash(0) {} 123 124 BreakpointOptions::BreakpointOptions(const char *condition, bool enabled, 125 int32_t ignore, bool one_shot) 126 : m_callback(nullptr), m_baton_is_command_baton(false), 127 m_callback_is_synchronous(false), m_enabled(enabled), 128 m_one_shot(one_shot), m_ignore_count(ignore), m_condition_text(condition), 129 m_condition_text_hash(0) 130 131 {} 132 133 //---------------------------------------------------------------------- 134 // BreakpointOptions copy constructor 135 //---------------------------------------------------------------------- 136 BreakpointOptions::BreakpointOptions(const BreakpointOptions &rhs) 137 : m_callback(rhs.m_callback), m_callback_baton_sp(rhs.m_callback_baton_sp), 138 m_baton_is_command_baton(rhs.m_baton_is_command_baton), 139 m_callback_is_synchronous(rhs.m_callback_is_synchronous), 140 m_enabled(rhs.m_enabled), m_one_shot(rhs.m_one_shot), 141 m_ignore_count(rhs.m_ignore_count), m_thread_spec_ap() { 142 if (rhs.m_thread_spec_ap.get() != nullptr) 143 m_thread_spec_ap.reset(new ThreadSpec(*rhs.m_thread_spec_ap.get())); 144 m_condition_text = rhs.m_condition_text; 145 m_condition_text_hash = rhs.m_condition_text_hash; 146 } 147 148 //---------------------------------------------------------------------- 149 // BreakpointOptions assignment operator 150 //---------------------------------------------------------------------- 151 const BreakpointOptions &BreakpointOptions:: 152 operator=(const BreakpointOptions &rhs) { 153 m_callback = rhs.m_callback; 154 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; 158 m_one_shot = rhs.m_one_shot; 159 m_ignore_count = rhs.m_ignore_count; 160 if (rhs.m_thread_spec_ap.get() != nullptr) 161 m_thread_spec_ap.reset(new ThreadSpec(*rhs.m_thread_spec_ap.get())); 162 m_condition_text = rhs.m_condition_text; 163 m_condition_text_hash = rhs.m_condition_text_hash; 164 return *this; 165 } 166 167 BreakpointOptions * 168 BreakpointOptions::CopyOptionsNoCallback(BreakpointOptions &orig) { 169 BreakpointHitCallback orig_callback = orig.m_callback; 170 lldb::BatonSP orig_callback_baton_sp = orig.m_callback_baton_sp; 171 bool orig_is_sync = orig.m_callback_is_synchronous; 172 173 orig.ClearCallback(); 174 BreakpointOptions *ret_val = new BreakpointOptions(orig); 175 176 orig.SetCallback(orig_callback, orig_callback_baton_sp, orig_is_sync); 177 178 return ret_val; 179 } 180 181 //---------------------------------------------------------------------- 182 // Destructor 183 //---------------------------------------------------------------------- 184 BreakpointOptions::~BreakpointOptions() = default; 185 186 std::unique_ptr<BreakpointOptions> BreakpointOptions::CreateFromStructuredData( 187 const StructuredData::Dictionary &options_dict, Error &error) { 188 bool enabled = true; 189 bool one_shot = false; 190 int32_t ignore_count = 0; 191 std::string condition_text; 192 193 bool success = options_dict.GetValueForKeyAsBoolean( 194 GetKey(OptionNames::EnabledState), enabled); 195 if (!success) { 196 error.SetErrorStringWithFormat("%s key is not a boolean.", 197 GetKey(OptionNames::EnabledState)); 198 return nullptr; 199 } 200 201 success = options_dict.GetValueForKeyAsBoolean( 202 GetKey(OptionNames::OneShotState), one_shot); 203 if (!success) { 204 error.SetErrorStringWithFormat("%s key is not a boolean.", 205 GetKey(OptionNames::OneShotState)); 206 return nullptr; 207 } 208 success = options_dict.GetValueForKeyAsInteger( 209 GetKey(OptionNames::IgnoreCount), ignore_count); 210 if (!success) { 211 error.SetErrorStringWithFormat("%s key is not an integer.", 212 GetKey(OptionNames::IgnoreCount)); 213 return nullptr; 214 } 215 216 std::unique_ptr<CommandData> cmd_data_up; 217 StructuredData::Dictionary *cmds_dict; 218 success = options_dict.GetValueForKeyAsDictionary( 219 CommandData::GetSerializationKey(), cmds_dict); 220 if (success && cmds_dict) { 221 Error cmds_error; 222 cmd_data_up = CommandData::CreateFromStructuredData(*cmds_dict, cmds_error); 223 if (cmds_error.Fail()) { 224 error.SetErrorStringWithFormat( 225 "Failed to deserialize breakpoint command options: %s.", 226 cmds_error.AsCString()); 227 return nullptr; 228 } 229 } 230 231 auto bp_options = llvm::make_unique<BreakpointOptions>( 232 condition_text.c_str(), enabled, ignore_count, one_shot); 233 if (cmd_data_up.get()) 234 bp_options->SetCommandDataCallback(cmd_data_up); 235 236 StructuredData::Dictionary *thread_spec_dict; 237 success = options_dict.GetValueForKeyAsDictionary( 238 ThreadSpec::GetSerializationKey(), thread_spec_dict); 239 if (success) { 240 Error thread_spec_error; 241 std::unique_ptr<ThreadSpec> thread_spec_up = 242 ThreadSpec::CreateFromStructuredData(*thread_spec_dict, 243 thread_spec_error); 244 if (thread_spec_error.Fail()) { 245 error.SetErrorStringWithFormat( 246 "Failed to deserialize breakpoint thread spec options: %s.", 247 thread_spec_error.AsCString()); 248 return nullptr; 249 } 250 bp_options->SetThreadSpec(thread_spec_up); 251 } 252 return bp_options; 253 } 254 255 StructuredData::ObjectSP BreakpointOptions::SerializeToStructuredData() { 256 StructuredData::DictionarySP options_dict_sp( 257 new StructuredData::Dictionary()); 258 options_dict_sp->AddBooleanItem(GetKey(OptionNames::EnabledState), m_enabled); 259 options_dict_sp->AddBooleanItem(GetKey(OptionNames::OneShotState), 260 m_one_shot); 261 options_dict_sp->AddIntegerItem(GetKey(OptionNames::IgnoreCount), 262 m_ignore_count); 263 options_dict_sp->AddStringItem(GetKey(OptionNames::ConditionText), 264 m_condition_text); 265 if (m_baton_is_command_baton) { 266 auto cmd_baton = 267 std::static_pointer_cast<CommandBaton>(m_callback_baton_sp); 268 StructuredData::ObjectSP commands_sp = 269 cmd_baton->getItem()->SerializeToStructuredData(); 270 if (commands_sp) { 271 options_dict_sp->AddItem( 272 BreakpointOptions::CommandData::GetSerializationKey(), commands_sp); 273 } 274 } 275 if (m_thread_spec_ap) { 276 StructuredData::ObjectSP thread_spec_sp = 277 m_thread_spec_ap->SerializeToStructuredData(); 278 options_dict_sp->AddItem(ThreadSpec::GetSerializationKey(), thread_spec_sp); 279 } 280 281 return options_dict_sp; 282 } 283 284 //------------------------------------------------------------------ 285 // Callbacks 286 //------------------------------------------------------------------ 287 void BreakpointOptions::SetCallback(BreakpointHitCallback callback, 288 const lldb::BatonSP &callback_baton_sp, 289 bool callback_is_synchronous) { 290 // FIXME: This seems unsafe. If BatonSP actually *is* a CommandBaton, but 291 // in a shared_ptr<Baton> instead of a shared_ptr<CommandBaton>, then we 292 // will set m_baton_is_command_baton to false, which is incorrect. 293 // One possible solution is to make the base Baton class provide a method 294 // such as: 295 // virtual StringRef getBatonId() const { return ""; } 296 // and have CommandBaton override this to return something unique, and then 297 // check for it here. Another option might be to make Baton using the llvm 298 // casting infrastructure, so that we could write something like: 299 // if (llvm::isa<CommandBaton>(callback_baton_sp)) 300 // at relevant callsites instead of storing a boolean. 301 m_callback_is_synchronous = callback_is_synchronous; 302 m_callback = callback; 303 m_callback_baton_sp = callback_baton_sp; 304 m_baton_is_command_baton = false; 305 } 306 307 void BreakpointOptions::SetCallback( 308 BreakpointHitCallback callback, 309 const BreakpointOptions::CommandBatonSP &callback_baton_sp, 310 bool callback_is_synchronous) { 311 m_callback_is_synchronous = callback_is_synchronous; 312 m_callback = callback; 313 m_callback_baton_sp = callback_baton_sp; 314 m_baton_is_command_baton = true; 315 } 316 317 void BreakpointOptions::ClearCallback() { 318 m_callback = BreakpointOptions::NullCallback; 319 m_callback_is_synchronous = false; 320 m_callback_baton_sp.reset(); 321 m_baton_is_command_baton = false; 322 } 323 324 Baton *BreakpointOptions::GetBaton() { return m_callback_baton_sp.get(); } 325 326 const Baton *BreakpointOptions::GetBaton() const { 327 return m_callback_baton_sp.get(); 328 } 329 330 bool BreakpointOptions::InvokeCallback(StoppointCallbackContext *context, 331 lldb::user_id_t break_id, 332 lldb::user_id_t break_loc_id) { 333 if (m_callback && context->is_synchronous == IsCallbackSynchronous()) { 334 return m_callback(m_callback_baton_sp ? m_callback_baton_sp->data() 335 : nullptr, 336 context, break_id, break_loc_id); 337 } else 338 return true; 339 } 340 341 bool BreakpointOptions::HasCallback() const { 342 return m_callback != BreakpointOptions::NullCallback; 343 } 344 345 bool BreakpointOptions::GetCommandLineCallbacks(StringList &command_list) { 346 if (!HasCallback()) 347 return false; 348 if (!m_baton_is_command_baton) 349 return false; 350 351 auto cmd_baton = std::static_pointer_cast<CommandBaton>(m_callback_baton_sp); 352 CommandData *data = cmd_baton->getItem(); 353 if (!data) 354 return false; 355 command_list = data->user_source; 356 return true; 357 } 358 359 void BreakpointOptions::SetCondition(const char *condition) { 360 if (!condition) 361 condition = ""; 362 363 m_condition_text.assign(condition); 364 std::hash<std::string> hasher; 365 m_condition_text_hash = hasher(m_condition_text); 366 } 367 368 const char *BreakpointOptions::GetConditionText(size_t *hash) const { 369 if (!m_condition_text.empty()) { 370 if (hash) 371 *hash = m_condition_text_hash; 372 373 return m_condition_text.c_str(); 374 } else { 375 return nullptr; 376 } 377 } 378 379 const ThreadSpec *BreakpointOptions::GetThreadSpecNoCreate() const { 380 return m_thread_spec_ap.get(); 381 } 382 383 ThreadSpec *BreakpointOptions::GetThreadSpec() { 384 if (m_thread_spec_ap.get() == nullptr) 385 m_thread_spec_ap.reset(new ThreadSpec()); 386 387 return m_thread_spec_ap.get(); 388 } 389 390 void BreakpointOptions::SetThreadID(lldb::tid_t thread_id) { 391 GetThreadSpec()->SetTID(thread_id); 392 } 393 394 void BreakpointOptions::SetThreadSpec( 395 std::unique_ptr<ThreadSpec> &thread_spec_up) { 396 m_thread_spec_ap = std::move(thread_spec_up); 397 } 398 399 void BreakpointOptions::GetDescription(Stream *s, 400 lldb::DescriptionLevel level) const { 401 // Figure out if there are any options not at their default value, and only 402 // print 403 // anything if there are: 404 405 if (m_ignore_count != 0 || !m_enabled || m_one_shot || 406 (GetThreadSpecNoCreate() != nullptr && 407 GetThreadSpecNoCreate()->HasSpecification())) { 408 if (level == lldb::eDescriptionLevelVerbose) { 409 s->EOL(); 410 s->IndentMore(); 411 s->Indent(); 412 s->PutCString("Breakpoint Options:\n"); 413 s->IndentMore(); 414 s->Indent(); 415 } else 416 s->PutCString(" Options: "); 417 418 if (m_ignore_count > 0) 419 s->Printf("ignore: %d ", m_ignore_count); 420 s->Printf("%sabled ", m_enabled ? "en" : "dis"); 421 422 if (m_one_shot) 423 s->Printf("one-shot "); 424 425 if (m_thread_spec_ap.get()) 426 m_thread_spec_ap->GetDescription(s, level); 427 428 if (level == lldb::eDescriptionLevelFull) { 429 s->IndentLess(); 430 s->IndentMore(); 431 } 432 } 433 434 if (m_callback_baton_sp.get()) { 435 if (level != eDescriptionLevelBrief) { 436 s->EOL(); 437 m_callback_baton_sp->GetDescription(s, level); 438 } 439 } 440 if (!m_condition_text.empty()) { 441 if (level != eDescriptionLevelBrief) { 442 s->EOL(); 443 s->Printf("Condition: %s\n", m_condition_text.c_str()); 444 } 445 } 446 } 447 448 void BreakpointOptions::CommandBaton::GetDescription( 449 Stream *s, lldb::DescriptionLevel level) const { 450 const CommandData *data = getItem(); 451 452 if (level == eDescriptionLevelBrief) { 453 s->Printf(", commands = %s", 454 (data && data->user_source.GetSize() > 0) ? "yes" : "no"); 455 return; 456 } 457 458 s->IndentMore(); 459 s->Indent("Breakpoint commands:\n"); 460 461 s->IndentMore(); 462 if (data && data->user_source.GetSize() > 0) { 463 const size_t num_strings = data->user_source.GetSize(); 464 for (size_t i = 0; i < num_strings; ++i) { 465 s->Indent(data->user_source.GetStringAtIndex(i)); 466 s->EOL(); 467 } 468 } else { 469 s->PutCString("No commands.\n"); 470 } 471 s->IndentLess(); 472 s->IndentLess(); 473 } 474 475 void BreakpointOptions::SetCommandDataCallback( 476 std::unique_ptr<CommandData> &cmd_data) { 477 auto baton_sp = std::make_shared<CommandBaton>(std::move(cmd_data)); 478 SetCallback(BreakpointOptions::BreakpointOptionsCallbackFunction, baton_sp); 479 } 480 481 bool BreakpointOptions::BreakpointOptionsCallbackFunction( 482 void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id, 483 lldb::user_id_t break_loc_id) { 484 bool ret_value = true; 485 if (baton == nullptr) 486 return true; 487 488 CommandData *data = (CommandData *)baton; 489 StringList &commands = data->user_source; 490 491 if (commands.GetSize() > 0) { 492 ExecutionContext exe_ctx(context->exe_ctx_ref); 493 Target *target = exe_ctx.GetTargetPtr(); 494 if (target) { 495 CommandReturnObject result; 496 Debugger &debugger = target->GetDebugger(); 497 // Rig up the results secondary output stream to the debugger's, so the 498 // output will come out synchronously 499 // if the debugger is set up that way. 500 501 StreamSP output_stream(debugger.GetAsyncOutputStream()); 502 StreamSP error_stream(debugger.GetAsyncErrorStream()); 503 result.SetImmediateOutputStream(output_stream); 504 result.SetImmediateErrorStream(error_stream); 505 506 CommandInterpreterRunOptions options; 507 options.SetStopOnContinue(true); 508 options.SetStopOnError(data->stop_on_error); 509 options.SetEchoCommands(true); 510 options.SetPrintResults(true); 511 options.SetAddToHistory(false); 512 513 debugger.GetCommandInterpreter().HandleCommands(commands, &exe_ctx, 514 options, result); 515 result.GetImmediateOutputStream()->Flush(); 516 result.GetImmediateErrorStream()->Flush(); 517 } 518 } 519 return ret_value; 520 } 521