1 //===-- CommandObjectWatchpointCommand.cpp --------------------------------===// 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 <vector> 10 11 #include "CommandObjectWatchpoint.h" 12 #include "CommandObjectWatchpointCommand.h" 13 #include "lldb/Breakpoint/StoppointCallbackContext.h" 14 #include "lldb/Breakpoint/Watchpoint.h" 15 #include "lldb/Core/IOHandler.h" 16 #include "lldb/Host/OptionParser.h" 17 #include "lldb/Interpreter/CommandInterpreter.h" 18 #include "lldb/Interpreter/CommandOptionArgumentTable.h" 19 #include "lldb/Interpreter/CommandReturnObject.h" 20 #include "lldb/Interpreter/OptionArgParser.h" 21 #include "lldb/Target/Target.h" 22 23 using namespace lldb; 24 using namespace lldb_private; 25 26 #define LLDB_OPTIONS_watchpoint_command_add 27 #include "CommandOptions.inc" 28 29 class CommandObjectWatchpointCommandAdd : public CommandObjectParsed, 30 public IOHandlerDelegateMultiline { 31 public: 32 CommandObjectWatchpointCommandAdd(CommandInterpreter &interpreter) 33 : CommandObjectParsed(interpreter, "add", 34 "Add a set of LLDB commands to a watchpoint, to be " 35 "executed whenever the watchpoint is hit. " 36 "The commands added to the watchpoint replace any " 37 "commands previously added to it.", 38 nullptr, eCommandRequiresTarget), 39 IOHandlerDelegateMultiline("DONE", 40 IOHandlerDelegate::Completion::LLDBCommand) { 41 SetHelpLong( 42 R"( 43 General information about entering watchpoint commands 44 ------------------------------------------------------ 45 46 )" 47 "This command will prompt for commands to be executed when the specified \ 48 watchpoint is hit. Each command is typed on its own line following the '> ' \ 49 prompt until 'DONE' is entered." 50 R"( 51 52 )" 53 "Syntactic errors may not be detected when initially entered, and many \ 54 malformed commands can silently fail when executed. If your watchpoint commands \ 55 do not appear to be executing, double-check the command syntax." 56 R"( 57 58 )" 59 "Note: You may enter any debugger command exactly as you would at the debugger \ 60 prompt. There is no limit to the number of commands supplied, but do NOT enter \ 61 more than one command per line." 62 R"( 63 64 Special information about PYTHON watchpoint commands 65 ---------------------------------------------------- 66 67 )" 68 "You may enter either one or more lines of Python, including function \ 69 definitions or calls to functions that will have been imported by the time \ 70 the code executes. Single line watchpoint commands will be interpreted 'as is' \ 71 when the watchpoint is hit. Multiple lines of Python will be wrapped in a \ 72 generated function, and a call to the function will be attached to the watchpoint." 73 R"( 74 75 This auto-generated function is passed in three arguments: 76 77 frame: an lldb.SBFrame object for the frame which hit the watchpoint. 78 79 wp: the watchpoint that was hit. 80 81 )" 82 "When specifying a python function with the --python-function option, you need \ 83 to supply the function name prepended by the module name:" 84 R"( 85 86 --python-function myutils.watchpoint_callback 87 88 The function itself must have the following prototype: 89 90 def watchpoint_callback(frame, wp): 91 # Your code goes here 92 93 )" 94 "The arguments are the same as the arguments passed to generated functions as \ 95 described above. Note that the global variable 'lldb.frame' will NOT be updated when \ 96 this function is called, so be sure to use the 'frame' argument. The 'frame' argument \ 97 can get you to the thread via frame.GetThread(), the thread can get you to the \ 98 process via thread.GetProcess(), and the process can get you back to the target \ 99 via process.GetTarget()." 100 R"( 101 102 )" 103 "Important Note: As Python code gets collected into functions, access to global \ 104 variables requires explicit scoping using the 'global' keyword. Be sure to use correct \ 105 Python syntax, including indentation, when entering Python watchpoint commands." 106 R"( 107 108 Example Python one-line watchpoint command: 109 110 (lldb) watchpoint command add -s python 1 111 Enter your Python command(s). Type 'DONE' to end. 112 > print "Hit this watchpoint!" 113 > DONE 114 115 As a convenience, this also works for a short Python one-liner: 116 117 (lldb) watchpoint command add -s python 1 -o 'import time; print time.asctime()' 118 (lldb) run 119 Launching '.../a.out' (x86_64) 120 (lldb) Fri Sep 10 12:17:45 2010 121 Process 21778 Stopped 122 * thread #1: tid = 0x2e03, 0x0000000100000de8 a.out`c + 7 at main.c:39, stop reason = watchpoint 1.1, queue = com.apple.main-thread 123 36 124 37 int c(int val) 125 38 { 126 39 -> return val + 3; 127 40 } 128 41 129 42 int main (int argc, char const *argv[]) 130 131 Example multiple line Python watchpoint command, using function definition: 132 133 (lldb) watchpoint command add -s python 1 134 Enter your Python command(s). Type 'DONE' to end. 135 > def watchpoint_output (wp_no): 136 > out_string = "Hit watchpoint number " + repr (wp_no) 137 > print out_string 138 > return True 139 > watchpoint_output (1) 140 > DONE 141 142 Example multiple line Python watchpoint command, using 'loose' Python: 143 144 (lldb) watchpoint command add -s p 1 145 Enter your Python command(s). Type 'DONE' to end. 146 > global wp_count 147 > wp_count = wp_count + 1 148 > print "Hit this watchpoint " + repr(wp_count) + " times!" 149 > DONE 150 151 )" 152 "In this case, since there is a reference to a global variable, \ 153 'wp_count', you will also need to make sure 'wp_count' exists and is \ 154 initialized:" 155 R"( 156 157 (lldb) script 158 >>> wp_count = 0 159 >>> quit() 160 161 )" 162 "Final Note: A warning that no watchpoint command was generated when there \ 163 are no syntax errors may indicate that a function was declared but never called."); 164 165 CommandArgumentEntry arg; 166 CommandArgumentData wp_id_arg; 167 168 // Define the first (and only) variant of this arg. 169 wp_id_arg.arg_type = eArgTypeWatchpointID; 170 wp_id_arg.arg_repetition = eArgRepeatPlain; 171 172 // There is only one variant this argument could be; put it into the 173 // argument entry. 174 arg.push_back(wp_id_arg); 175 176 // Push the data for the first argument into the m_arguments vector. 177 m_arguments.push_back(arg); 178 } 179 180 ~CommandObjectWatchpointCommandAdd() override = default; 181 182 Options *GetOptions() override { return &m_options; } 183 184 void IOHandlerActivated(IOHandler &io_handler, bool interactive) override { 185 StreamFileSP output_sp(io_handler.GetOutputStreamFileSP()); 186 if (output_sp && interactive) { 187 output_sp->PutCString( 188 "Enter your debugger command(s). Type 'DONE' to end.\n"); 189 output_sp->Flush(); 190 } 191 } 192 193 void IOHandlerInputComplete(IOHandler &io_handler, 194 std::string &line) override { 195 io_handler.SetIsDone(true); 196 197 // The WatchpointOptions object is owned by the watchpoint or watchpoint 198 // location 199 WatchpointOptions *wp_options = 200 (WatchpointOptions *)io_handler.GetUserData(); 201 if (wp_options) { 202 std::unique_ptr<WatchpointOptions::CommandData> data_up( 203 new WatchpointOptions::CommandData()); 204 if (data_up) { 205 data_up->user_source.SplitIntoLines(line); 206 auto baton_sp = std::make_shared<WatchpointOptions::CommandBaton>( 207 std::move(data_up)); 208 wp_options->SetCallback(WatchpointOptionsCallbackFunction, baton_sp); 209 } 210 } 211 } 212 213 void CollectDataForWatchpointCommandCallback(WatchpointOptions *wp_options, 214 CommandReturnObject &result) { 215 m_interpreter.GetLLDBCommandsFromIOHandler( 216 "> ", // Prompt 217 *this, // IOHandlerDelegate 218 wp_options); // Baton for the "io_handler" that will be passed back into 219 // our IOHandlerDelegate functions 220 } 221 222 /// Set a one-liner as the callback for the watchpoint. 223 void SetWatchpointCommandCallback(WatchpointOptions *wp_options, 224 const char *oneliner) { 225 std::unique_ptr<WatchpointOptions::CommandData> data_up( 226 new WatchpointOptions::CommandData()); 227 228 // It's necessary to set both user_source and script_source to the 229 // oneliner. The former is used to generate callback description (as in 230 // watchpoint command list) while the latter is used for Python to 231 // interpret during the actual callback. 232 data_up->user_source.AppendString(oneliner); 233 data_up->script_source.assign(oneliner); 234 data_up->stop_on_error = m_options.m_stop_on_error; 235 236 auto baton_sp = 237 std::make_shared<WatchpointOptions::CommandBaton>(std::move(data_up)); 238 wp_options->SetCallback(WatchpointOptionsCallbackFunction, baton_sp); 239 } 240 241 static bool 242 WatchpointOptionsCallbackFunction(void *baton, 243 StoppointCallbackContext *context, 244 lldb::user_id_t watch_id) { 245 bool ret_value = true; 246 if (baton == nullptr) 247 return true; 248 249 WatchpointOptions::CommandData *data = 250 (WatchpointOptions::CommandData *)baton; 251 StringList &commands = data->user_source; 252 253 if (commands.GetSize() > 0) { 254 ExecutionContext exe_ctx(context->exe_ctx_ref); 255 Target *target = exe_ctx.GetTargetPtr(); 256 if (target) { 257 Debugger &debugger = target->GetDebugger(); 258 CommandReturnObject result(debugger.GetUseColor()); 259 260 // Rig up the results secondary output stream to the debugger's, so the 261 // output will come out synchronously if the debugger is set up that 262 // way. 263 StreamSP output_stream(debugger.GetAsyncOutputStream()); 264 StreamSP error_stream(debugger.GetAsyncErrorStream()); 265 result.SetImmediateOutputStream(output_stream); 266 result.SetImmediateErrorStream(error_stream); 267 268 CommandInterpreterRunOptions options; 269 options.SetStopOnContinue(true); 270 options.SetStopOnError(data->stop_on_error); 271 options.SetEchoCommands(false); 272 options.SetPrintResults(true); 273 options.SetPrintErrors(true); 274 options.SetAddToHistory(false); 275 276 debugger.GetCommandInterpreter().HandleCommands(commands, exe_ctx, 277 options, result); 278 result.GetImmediateOutputStream()->Flush(); 279 result.GetImmediateErrorStream()->Flush(); 280 } 281 } 282 return ret_value; 283 } 284 285 class CommandOptions : public Options { 286 public: 287 CommandOptions() = default; 288 289 ~CommandOptions() override = default; 290 291 Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, 292 ExecutionContext *execution_context) override { 293 Status error; 294 const int short_option = m_getopt_table[option_idx].val; 295 296 switch (short_option) { 297 case 'o': 298 m_use_one_liner = true; 299 m_one_liner = std::string(option_arg); 300 break; 301 302 case 's': 303 m_script_language = (lldb::ScriptLanguage)OptionArgParser::ToOptionEnum( 304 option_arg, GetDefinitions()[option_idx].enum_values, 305 eScriptLanguageNone, error); 306 307 switch (m_script_language) { 308 case eScriptLanguagePython: 309 case eScriptLanguageLua: 310 m_use_script_language = true; 311 break; 312 case eScriptLanguageNone: 313 case eScriptLanguageUnknown: 314 m_use_script_language = false; 315 break; 316 } 317 break; 318 319 case 'e': { 320 bool success = false; 321 m_stop_on_error = 322 OptionArgParser::ToBoolean(option_arg, false, &success); 323 if (!success) 324 error.SetErrorStringWithFormat( 325 "invalid value for stop-on-error: \"%s\"", 326 option_arg.str().c_str()); 327 } break; 328 329 case 'F': 330 m_use_one_liner = false; 331 m_function_name.assign(std::string(option_arg)); 332 break; 333 334 default: 335 llvm_unreachable("Unimplemented option"); 336 } 337 return error; 338 } 339 340 void OptionParsingStarting(ExecutionContext *execution_context) override { 341 m_use_commands = true; 342 m_use_script_language = false; 343 m_script_language = eScriptLanguageNone; 344 345 m_use_one_liner = false; 346 m_stop_on_error = true; 347 m_one_liner.clear(); 348 m_function_name.clear(); 349 } 350 351 llvm::ArrayRef<OptionDefinition> GetDefinitions() override { 352 return llvm::ArrayRef(g_watchpoint_command_add_options); 353 } 354 355 // Instance variables to hold the values for command options. 356 357 bool m_use_commands = false; 358 bool m_use_script_language = false; 359 lldb::ScriptLanguage m_script_language = eScriptLanguageNone; 360 361 // Instance variables to hold the values for one_liner options. 362 bool m_use_one_liner = false; 363 std::string m_one_liner; 364 bool m_stop_on_error; 365 std::string m_function_name; 366 }; 367 368 protected: 369 bool DoExecute(Args &command, CommandReturnObject &result) override { 370 Target *target = &GetSelectedTarget(); 371 372 const WatchpointList &watchpoints = target->GetWatchpointList(); 373 size_t num_watchpoints = watchpoints.GetSize(); 374 375 if (num_watchpoints == 0) { 376 result.AppendError("No watchpoints exist to have commands added"); 377 return false; 378 } 379 380 if (!m_options.m_function_name.empty()) { 381 if (!m_options.m_use_script_language) { 382 m_options.m_script_language = GetDebugger().GetScriptLanguage(); 383 m_options.m_use_script_language = true; 384 } 385 } 386 387 std::vector<uint32_t> valid_wp_ids; 388 if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, command, 389 valid_wp_ids)) { 390 result.AppendError("Invalid watchpoints specification."); 391 return false; 392 } 393 394 result.SetStatus(eReturnStatusSuccessFinishNoResult); 395 const size_t count = valid_wp_ids.size(); 396 for (size_t i = 0; i < count; ++i) { 397 uint32_t cur_wp_id = valid_wp_ids.at(i); 398 if (cur_wp_id != LLDB_INVALID_WATCH_ID) { 399 Watchpoint *wp = target->GetWatchpointList().FindByID(cur_wp_id).get(); 400 // Sanity check wp first. 401 if (wp == nullptr) 402 continue; 403 404 WatchpointOptions *wp_options = wp->GetOptions(); 405 // Skip this watchpoint if wp_options is not good. 406 if (wp_options == nullptr) 407 continue; 408 409 // If we are using script language, get the script interpreter in order 410 // to set or collect command callback. Otherwise, call the methods 411 // associated with this object. 412 if (m_options.m_use_script_language) { 413 ScriptInterpreter *script_interp = GetDebugger().GetScriptInterpreter( 414 /*can_create=*/true, m_options.m_script_language); 415 // Special handling for one-liner specified inline. 416 if (m_options.m_use_one_liner) { 417 script_interp->SetWatchpointCommandCallback( 418 wp_options, m_options.m_one_liner.c_str(), 419 /*is_callback=*/false); 420 } 421 // Special handling for using a Python function by name instead of 422 // extending the watchpoint callback data structures, we just 423 // automatize what the user would do manually: make their watchpoint 424 // command be a function call 425 else if (!m_options.m_function_name.empty()) { 426 std::string function_signature = m_options.m_function_name; 427 function_signature += "(frame, wp, internal_dict)"; 428 script_interp->SetWatchpointCommandCallback( 429 wp_options, function_signature.c_str(), /*is_callback=*/true); 430 } else { 431 script_interp->CollectDataForWatchpointCommandCallback(wp_options, 432 result); 433 } 434 } else { 435 // Special handling for one-liner specified inline. 436 if (m_options.m_use_one_liner) 437 SetWatchpointCommandCallback(wp_options, 438 m_options.m_one_liner.c_str()); 439 else 440 CollectDataForWatchpointCommandCallback(wp_options, result); 441 } 442 } 443 } 444 445 return result.Succeeded(); 446 } 447 448 private: 449 CommandOptions m_options; 450 }; 451 452 // CommandObjectWatchpointCommandDelete 453 454 class CommandObjectWatchpointCommandDelete : public CommandObjectParsed { 455 public: 456 CommandObjectWatchpointCommandDelete(CommandInterpreter &interpreter) 457 : CommandObjectParsed(interpreter, "delete", 458 "Delete the set of commands from a watchpoint.", 459 nullptr, eCommandRequiresTarget) { 460 CommandArgumentEntry arg; 461 CommandArgumentData wp_id_arg; 462 463 // Define the first (and only) variant of this arg. 464 wp_id_arg.arg_type = eArgTypeWatchpointID; 465 wp_id_arg.arg_repetition = eArgRepeatPlain; 466 467 // There is only one variant this argument could be; put it into the 468 // argument entry. 469 arg.push_back(wp_id_arg); 470 471 // Push the data for the first argument into the m_arguments vector. 472 m_arguments.push_back(arg); 473 } 474 475 ~CommandObjectWatchpointCommandDelete() override = default; 476 477 protected: 478 bool DoExecute(Args &command, CommandReturnObject &result) override { 479 Target *target = &GetSelectedTarget(); 480 481 const WatchpointList &watchpoints = target->GetWatchpointList(); 482 size_t num_watchpoints = watchpoints.GetSize(); 483 484 if (num_watchpoints == 0) { 485 result.AppendError("No watchpoints exist to have commands deleted"); 486 return false; 487 } 488 489 if (command.GetArgumentCount() == 0) { 490 result.AppendError( 491 "No watchpoint specified from which to delete the commands"); 492 return false; 493 } 494 495 std::vector<uint32_t> valid_wp_ids; 496 if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, command, 497 valid_wp_ids)) { 498 result.AppendError("Invalid watchpoints specification."); 499 return false; 500 } 501 502 result.SetStatus(eReturnStatusSuccessFinishNoResult); 503 const size_t count = valid_wp_ids.size(); 504 for (size_t i = 0; i < count; ++i) { 505 uint32_t cur_wp_id = valid_wp_ids.at(i); 506 if (cur_wp_id != LLDB_INVALID_WATCH_ID) { 507 Watchpoint *wp = target->GetWatchpointList().FindByID(cur_wp_id).get(); 508 if (wp) 509 wp->ClearCallback(); 510 } else { 511 result.AppendErrorWithFormat("Invalid watchpoint ID: %u.\n", cur_wp_id); 512 return false; 513 } 514 } 515 return result.Succeeded(); 516 } 517 }; 518 519 // CommandObjectWatchpointCommandList 520 521 class CommandObjectWatchpointCommandList : public CommandObjectParsed { 522 public: 523 CommandObjectWatchpointCommandList(CommandInterpreter &interpreter) 524 : CommandObjectParsed(interpreter, "list", 525 "List the script or set of commands to be executed " 526 "when the watchpoint is hit.", 527 nullptr, eCommandRequiresTarget) { 528 CommandArgumentEntry arg; 529 CommandArgumentData wp_id_arg; 530 531 // Define the first (and only) variant of this arg. 532 wp_id_arg.arg_type = eArgTypeWatchpointID; 533 wp_id_arg.arg_repetition = eArgRepeatPlain; 534 535 // There is only one variant this argument could be; put it into the 536 // argument entry. 537 arg.push_back(wp_id_arg); 538 539 // Push the data for the first argument into the m_arguments vector. 540 m_arguments.push_back(arg); 541 } 542 543 ~CommandObjectWatchpointCommandList() override = default; 544 545 protected: 546 bool DoExecute(Args &command, CommandReturnObject &result) override { 547 Target *target = &GetSelectedTarget(); 548 549 const WatchpointList &watchpoints = target->GetWatchpointList(); 550 size_t num_watchpoints = watchpoints.GetSize(); 551 552 if (num_watchpoints == 0) { 553 result.AppendError("No watchpoints exist for which to list commands"); 554 return false; 555 } 556 557 if (command.GetArgumentCount() == 0) { 558 result.AppendError( 559 "No watchpoint specified for which to list the commands"); 560 return false; 561 } 562 563 std::vector<uint32_t> valid_wp_ids; 564 if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, command, 565 valid_wp_ids)) { 566 result.AppendError("Invalid watchpoints specification."); 567 return false; 568 } 569 570 result.SetStatus(eReturnStatusSuccessFinishNoResult); 571 const size_t count = valid_wp_ids.size(); 572 for (size_t i = 0; i < count; ++i) { 573 uint32_t cur_wp_id = valid_wp_ids.at(i); 574 if (cur_wp_id != LLDB_INVALID_WATCH_ID) { 575 Watchpoint *wp = target->GetWatchpointList().FindByID(cur_wp_id).get(); 576 577 if (wp) { 578 const WatchpointOptions *wp_options = wp->GetOptions(); 579 if (wp_options) { 580 // Get the callback baton associated with the current watchpoint. 581 const Baton *baton = wp_options->GetBaton(); 582 if (baton) { 583 result.GetOutputStream().Printf("Watchpoint %u:\n", cur_wp_id); 584 baton->GetDescription(result.GetOutputStream().AsRawOstream(), 585 eDescriptionLevelFull, 586 result.GetOutputStream().GetIndentLevel() + 587 2); 588 } else { 589 result.AppendMessageWithFormat( 590 "Watchpoint %u does not have an associated command.\n", 591 cur_wp_id); 592 } 593 } 594 result.SetStatus(eReturnStatusSuccessFinishResult); 595 } else { 596 result.AppendErrorWithFormat("Invalid watchpoint ID: %u.\n", 597 cur_wp_id); 598 } 599 } 600 } 601 602 return result.Succeeded(); 603 } 604 }; 605 606 // CommandObjectWatchpointCommand 607 608 CommandObjectWatchpointCommand::CommandObjectWatchpointCommand( 609 CommandInterpreter &interpreter) 610 : CommandObjectMultiword( 611 interpreter, "command", 612 "Commands for adding, removing and examining LLDB commands " 613 "executed when the watchpoint is hit (watchpoint 'commands').", 614 "command <sub-command> [<sub-command-options>] <watchpoint-id>") { 615 CommandObjectSP add_command_object( 616 new CommandObjectWatchpointCommandAdd(interpreter)); 617 CommandObjectSP delete_command_object( 618 new CommandObjectWatchpointCommandDelete(interpreter)); 619 CommandObjectSP list_command_object( 620 new CommandObjectWatchpointCommandList(interpreter)); 621 622 add_command_object->SetCommandName("watchpoint command add"); 623 delete_command_object->SetCommandName("watchpoint command delete"); 624 list_command_object->SetCommandName("watchpoint command list"); 625 626 LoadSubCommand("add", add_command_object); 627 LoadSubCommand("delete", delete_command_object); 628 LoadSubCommand("list", list_command_object); 629 } 630 631 CommandObjectWatchpointCommand::~CommandObjectWatchpointCommand() = default; 632