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 [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 return bp_options; 236 } 237 238 StructuredData::ObjectSP BreakpointOptions::SerializeToStructuredData() { 239 StructuredData::DictionarySP options_dict_sp( 240 new StructuredData::Dictionary()); 241 options_dict_sp->AddBooleanItem(GetKey(OptionNames::EnabledState), m_enabled); 242 options_dict_sp->AddBooleanItem(GetKey(OptionNames::OneShotState), 243 m_one_shot); 244 options_dict_sp->AddIntegerItem(GetKey(OptionNames::IgnoreCount), 245 m_ignore_count); 246 options_dict_sp->AddStringItem(GetKey(OptionNames::ConditionText), 247 m_condition_text); 248 if (m_baton_is_command_baton) { 249 auto cmd_baton = 250 std::static_pointer_cast<CommandBaton>(m_callback_baton_sp); 251 StructuredData::ObjectSP commands_sp = 252 cmd_baton->getItem()->SerializeToStructuredData(); 253 if (commands_sp) { 254 options_dict_sp->AddItem( 255 BreakpointOptions::CommandData::GetSerializationKey(), commands_sp); 256 } 257 } 258 // FIXME: Need to serialize thread filter... 259 return options_dict_sp; 260 } 261 262 //------------------------------------------------------------------ 263 // Callbacks 264 //------------------------------------------------------------------ 265 void BreakpointOptions::SetCallback(BreakpointHitCallback callback, 266 const lldb::BatonSP &callback_baton_sp, 267 bool callback_is_synchronous) { 268 // FIXME: This seems unsafe. If BatonSP actually *is* a CommandBaton, but 269 // in a shared_ptr<Baton> instead of a shared_ptr<CommandBaton>, then we 270 // will set m_baton_is_command_baton to false, which is incorrect. 271 // One possible solution is to make the base Baton class provide a method 272 // such as: 273 // virtual StringRef getBatonId() const { return ""; } 274 // and have CommandBaton override this to return something unique, and then 275 // check for it here. Another option might be to make Baton using the llvm 276 // casting infrastructure, so that we could write something like: 277 // if (llvm::isa<CommandBaton>(callback_baton_sp)) 278 // at relevant callsites instead of storing a boolean. 279 m_callback_is_synchronous = callback_is_synchronous; 280 m_callback = callback; 281 m_callback_baton_sp = callback_baton_sp; 282 m_baton_is_command_baton = false; 283 } 284 285 void BreakpointOptions::SetCallback( 286 BreakpointHitCallback callback, 287 const BreakpointOptions::CommandBatonSP &callback_baton_sp, 288 bool callback_is_synchronous) { 289 m_callback_is_synchronous = callback_is_synchronous; 290 m_callback = callback; 291 m_callback_baton_sp = callback_baton_sp; 292 m_baton_is_command_baton = true; 293 } 294 295 void BreakpointOptions::ClearCallback() { 296 m_callback = BreakpointOptions::NullCallback; 297 m_callback_is_synchronous = false; 298 m_callback_baton_sp.reset(); 299 m_baton_is_command_baton = false; 300 } 301 302 Baton *BreakpointOptions::GetBaton() { return m_callback_baton_sp.get(); } 303 304 const Baton *BreakpointOptions::GetBaton() const { 305 return m_callback_baton_sp.get(); 306 } 307 308 bool BreakpointOptions::InvokeCallback(StoppointCallbackContext *context, 309 lldb::user_id_t break_id, 310 lldb::user_id_t break_loc_id) { 311 if (m_callback && context->is_synchronous == IsCallbackSynchronous()) { 312 return m_callback(m_callback_baton_sp ? m_callback_baton_sp->data() 313 : nullptr, 314 context, break_id, break_loc_id); 315 } else 316 return true; 317 } 318 319 bool BreakpointOptions::HasCallback() const { 320 return m_callback != BreakpointOptions::NullCallback; 321 } 322 323 bool BreakpointOptions::GetCommandLineCallbacks(StringList &command_list) { 324 if (!HasCallback()) 325 return false; 326 if (!m_baton_is_command_baton) 327 return false; 328 329 auto cmd_baton = std::static_pointer_cast<CommandBaton>(m_callback_baton_sp); 330 CommandData *data = cmd_baton->getItem(); 331 if (!data) 332 return false; 333 command_list = data->user_source; 334 return true; 335 } 336 337 void BreakpointOptions::SetCondition(const char *condition) { 338 if (!condition) 339 condition = ""; 340 341 m_condition_text.assign(condition); 342 std::hash<std::string> hasher; 343 m_condition_text_hash = hasher(m_condition_text); 344 } 345 346 const char *BreakpointOptions::GetConditionText(size_t *hash) const { 347 if (!m_condition_text.empty()) { 348 if (hash) 349 *hash = m_condition_text_hash; 350 351 return m_condition_text.c_str(); 352 } else { 353 return nullptr; 354 } 355 } 356 357 const ThreadSpec *BreakpointOptions::GetThreadSpecNoCreate() const { 358 return m_thread_spec_ap.get(); 359 } 360 361 ThreadSpec *BreakpointOptions::GetThreadSpec() { 362 if (m_thread_spec_ap.get() == nullptr) 363 m_thread_spec_ap.reset(new ThreadSpec()); 364 365 return m_thread_spec_ap.get(); 366 } 367 368 void BreakpointOptions::SetThreadID(lldb::tid_t thread_id) { 369 GetThreadSpec()->SetTID(thread_id); 370 } 371 372 void BreakpointOptions::GetDescription(Stream *s, 373 lldb::DescriptionLevel level) const { 374 // Figure out if there are any options not at their default value, and only 375 // print 376 // anything if there are: 377 378 if (m_ignore_count != 0 || !m_enabled || m_one_shot || 379 (GetThreadSpecNoCreate() != nullptr && 380 GetThreadSpecNoCreate()->HasSpecification())) { 381 if (level == lldb::eDescriptionLevelVerbose) { 382 s->EOL(); 383 s->IndentMore(); 384 s->Indent(); 385 s->PutCString("Breakpoint Options:\n"); 386 s->IndentMore(); 387 s->Indent(); 388 } else 389 s->PutCString(" Options: "); 390 391 if (m_ignore_count > 0) 392 s->Printf("ignore: %d ", m_ignore_count); 393 s->Printf("%sabled ", m_enabled ? "en" : "dis"); 394 395 if (m_one_shot) 396 s->Printf("one-shot "); 397 398 if (m_thread_spec_ap.get()) 399 m_thread_spec_ap->GetDescription(s, level); 400 401 if (level == lldb::eDescriptionLevelFull) { 402 s->IndentLess(); 403 s->IndentMore(); 404 } 405 } 406 407 if (m_callback_baton_sp.get()) { 408 if (level != eDescriptionLevelBrief) { 409 s->EOL(); 410 m_callback_baton_sp->GetDescription(s, level); 411 } 412 } 413 if (!m_condition_text.empty()) { 414 if (level != eDescriptionLevelBrief) { 415 s->EOL(); 416 s->Printf("Condition: %s\n", m_condition_text.c_str()); 417 } 418 } 419 } 420 421 void BreakpointOptions::CommandBaton::GetDescription( 422 Stream *s, lldb::DescriptionLevel level) const { 423 const CommandData *data = getItem(); 424 425 if (level == eDescriptionLevelBrief) { 426 s->Printf(", commands = %s", 427 (data && data->user_source.GetSize() > 0) ? "yes" : "no"); 428 return; 429 } 430 431 s->IndentMore(); 432 s->Indent("Breakpoint commands:\n"); 433 434 s->IndentMore(); 435 if (data && data->user_source.GetSize() > 0) { 436 const size_t num_strings = data->user_source.GetSize(); 437 for (size_t i = 0; i < num_strings; ++i) { 438 s->Indent(data->user_source.GetStringAtIndex(i)); 439 s->EOL(); 440 } 441 } else { 442 s->PutCString("No commands.\n"); 443 } 444 s->IndentLess(); 445 s->IndentLess(); 446 } 447 448 void BreakpointOptions::SetCommandDataCallback( 449 std::unique_ptr<CommandData> &cmd_data) { 450 auto baton_sp = std::make_shared<CommandBaton>(std::move(cmd_data)); 451 SetCallback(BreakpointOptions::BreakpointOptionsCallbackFunction, baton_sp); 452 } 453 454 bool BreakpointOptions::BreakpointOptionsCallbackFunction( 455 void *baton, StoppointCallbackContext *context, lldb::user_id_t break_id, 456 lldb::user_id_t break_loc_id) { 457 bool ret_value = true; 458 if (baton == nullptr) 459 return true; 460 461 CommandData *data = (CommandData *)baton; 462 StringList &commands = data->user_source; 463 464 if (commands.GetSize() > 0) { 465 ExecutionContext exe_ctx(context->exe_ctx_ref); 466 Target *target = exe_ctx.GetTargetPtr(); 467 if (target) { 468 CommandReturnObject result; 469 Debugger &debugger = target->GetDebugger(); 470 // Rig up the results secondary output stream to the debugger's, so the 471 // output will come out synchronously 472 // if the debugger is set up that way. 473 474 StreamSP output_stream(debugger.GetAsyncOutputStream()); 475 StreamSP error_stream(debugger.GetAsyncErrorStream()); 476 result.SetImmediateOutputStream(output_stream); 477 result.SetImmediateErrorStream(error_stream); 478 479 CommandInterpreterRunOptions options; 480 options.SetStopOnContinue(true); 481 options.SetStopOnError(data->stop_on_error); 482 options.SetEchoCommands(true); 483 options.SetPrintResults(true); 484 options.SetAddToHistory(false); 485 486 debugger.GetCommandInterpreter().HandleCommands(commands, &exe_ctx, 487 options, result); 488 result.GetImmediateOutputStream()->Flush(); 489 result.GetImmediateErrorStream()->Flush(); 490 } 491 } 492 return ret_value; 493 } 494