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