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 } 420 // Special handling for using a Python function by name instead of 421 // extending the watchpoint callback data structures, we just 422 // automatize what the user would do manually: make their watchpoint 423 // command be a function call 424 else if (!m_options.m_function_name.empty()) { 425 std::string oneliner(m_options.m_function_name); 426 oneliner += "(frame, wp, internal_dict)"; 427 script_interp->SetWatchpointCommandCallback( 428 wp_options, oneliner.c_str()); 429 } else { 430 script_interp->CollectDataForWatchpointCommandCallback(wp_options, 431 result); 432 } 433 } else { 434 // Special handling for one-liner specified inline. 435 if (m_options.m_use_one_liner) 436 SetWatchpointCommandCallback(wp_options, 437 m_options.m_one_liner.c_str()); 438 else 439 CollectDataForWatchpointCommandCallback(wp_options, result); 440 } 441 } 442 } 443 444 return result.Succeeded(); 445 } 446 447 private: 448 CommandOptions m_options; 449 }; 450 451 // CommandObjectWatchpointCommandDelete 452 453 class CommandObjectWatchpointCommandDelete : public CommandObjectParsed { 454 public: 455 CommandObjectWatchpointCommandDelete(CommandInterpreter &interpreter) 456 : CommandObjectParsed(interpreter, "delete", 457 "Delete the set of commands from a watchpoint.", 458 nullptr, eCommandRequiresTarget) { 459 CommandArgumentEntry arg; 460 CommandArgumentData wp_id_arg; 461 462 // Define the first (and only) variant of this arg. 463 wp_id_arg.arg_type = eArgTypeWatchpointID; 464 wp_id_arg.arg_repetition = eArgRepeatPlain; 465 466 // There is only one variant this argument could be; put it into the 467 // argument entry. 468 arg.push_back(wp_id_arg); 469 470 // Push the data for the first argument into the m_arguments vector. 471 m_arguments.push_back(arg); 472 } 473 474 ~CommandObjectWatchpointCommandDelete() override = default; 475 476 protected: 477 bool DoExecute(Args &command, CommandReturnObject &result) override { 478 Target *target = &GetSelectedTarget(); 479 480 const WatchpointList &watchpoints = target->GetWatchpointList(); 481 size_t num_watchpoints = watchpoints.GetSize(); 482 483 if (num_watchpoints == 0) { 484 result.AppendError("No watchpoints exist to have commands deleted"); 485 return false; 486 } 487 488 if (command.GetArgumentCount() == 0) { 489 result.AppendError( 490 "No watchpoint specified from which to delete the commands"); 491 return false; 492 } 493 494 std::vector<uint32_t> valid_wp_ids; 495 if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, command, 496 valid_wp_ids)) { 497 result.AppendError("Invalid watchpoints specification."); 498 return false; 499 } 500 501 result.SetStatus(eReturnStatusSuccessFinishNoResult); 502 const size_t count = valid_wp_ids.size(); 503 for (size_t i = 0; i < count; ++i) { 504 uint32_t cur_wp_id = valid_wp_ids.at(i); 505 if (cur_wp_id != LLDB_INVALID_WATCH_ID) { 506 Watchpoint *wp = target->GetWatchpointList().FindByID(cur_wp_id).get(); 507 if (wp) 508 wp->ClearCallback(); 509 } else { 510 result.AppendErrorWithFormat("Invalid watchpoint ID: %u.\n", cur_wp_id); 511 return false; 512 } 513 } 514 return result.Succeeded(); 515 } 516 }; 517 518 // CommandObjectWatchpointCommandList 519 520 class CommandObjectWatchpointCommandList : public CommandObjectParsed { 521 public: 522 CommandObjectWatchpointCommandList(CommandInterpreter &interpreter) 523 : CommandObjectParsed(interpreter, "list", 524 "List the script or set of commands to be executed " 525 "when the watchpoint is hit.", 526 nullptr, eCommandRequiresTarget) { 527 CommandArgumentEntry arg; 528 CommandArgumentData wp_id_arg; 529 530 // Define the first (and only) variant of this arg. 531 wp_id_arg.arg_type = eArgTypeWatchpointID; 532 wp_id_arg.arg_repetition = eArgRepeatPlain; 533 534 // There is only one variant this argument could be; put it into the 535 // argument entry. 536 arg.push_back(wp_id_arg); 537 538 // Push the data for the first argument into the m_arguments vector. 539 m_arguments.push_back(arg); 540 } 541 542 ~CommandObjectWatchpointCommandList() override = default; 543 544 protected: 545 bool DoExecute(Args &command, CommandReturnObject &result) override { 546 Target *target = &GetSelectedTarget(); 547 548 const WatchpointList &watchpoints = target->GetWatchpointList(); 549 size_t num_watchpoints = watchpoints.GetSize(); 550 551 if (num_watchpoints == 0) { 552 result.AppendError("No watchpoints exist for which to list commands"); 553 return false; 554 } 555 556 if (command.GetArgumentCount() == 0) { 557 result.AppendError( 558 "No watchpoint specified for which to list the commands"); 559 return false; 560 } 561 562 std::vector<uint32_t> valid_wp_ids; 563 if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, command, 564 valid_wp_ids)) { 565 result.AppendError("Invalid watchpoints specification."); 566 return false; 567 } 568 569 result.SetStatus(eReturnStatusSuccessFinishNoResult); 570 const size_t count = valid_wp_ids.size(); 571 for (size_t i = 0; i < count; ++i) { 572 uint32_t cur_wp_id = valid_wp_ids.at(i); 573 if (cur_wp_id != LLDB_INVALID_WATCH_ID) { 574 Watchpoint *wp = target->GetWatchpointList().FindByID(cur_wp_id).get(); 575 576 if (wp) { 577 const WatchpointOptions *wp_options = wp->GetOptions(); 578 if (wp_options) { 579 // Get the callback baton associated with the current watchpoint. 580 const Baton *baton = wp_options->GetBaton(); 581 if (baton) { 582 result.GetOutputStream().Printf("Watchpoint %u:\n", cur_wp_id); 583 baton->GetDescription(result.GetOutputStream().AsRawOstream(), 584 eDescriptionLevelFull, 585 result.GetOutputStream().GetIndentLevel() + 586 2); 587 } else { 588 result.AppendMessageWithFormat( 589 "Watchpoint %u does not have an associated command.\n", 590 cur_wp_id); 591 } 592 } 593 result.SetStatus(eReturnStatusSuccessFinishResult); 594 } else { 595 result.AppendErrorWithFormat("Invalid watchpoint ID: %u.\n", 596 cur_wp_id); 597 } 598 } 599 } 600 601 return result.Succeeded(); 602 } 603 }; 604 605 // CommandObjectWatchpointCommand 606 607 CommandObjectWatchpointCommand::CommandObjectWatchpointCommand( 608 CommandInterpreter &interpreter) 609 : CommandObjectMultiword( 610 interpreter, "command", 611 "Commands for adding, removing and examining LLDB commands " 612 "executed when the watchpoint is hit (watchpoint 'commands').", 613 "command <sub-command> [<sub-command-options>] <watchpoint-id>") { 614 CommandObjectSP add_command_object( 615 new CommandObjectWatchpointCommandAdd(interpreter)); 616 CommandObjectSP delete_command_object( 617 new CommandObjectWatchpointCommandDelete(interpreter)); 618 CommandObjectSP list_command_object( 619 new CommandObjectWatchpointCommandList(interpreter)); 620 621 add_command_object->SetCommandName("watchpoint command add"); 622 delete_command_object->SetCommandName("watchpoint command delete"); 623 list_command_object->SetCommandName("watchpoint command list"); 624 625 LoadSubCommand("add", add_command_object); 626 LoadSubCommand("delete", delete_command_object); 627 LoadSubCommand("list", list_command_object); 628 } 629 630 CommandObjectWatchpointCommand::~CommandObjectWatchpointCommand() = default; 631