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 | AddSimpleArgumentList(arg_type: eArgTypeWatchpointID); |
166 | } |
167 | |
168 | ~CommandObjectWatchpointCommandAdd() override = default; |
169 | |
170 | Options *GetOptions() override { return &m_options; } |
171 | |
172 | void IOHandlerActivated(IOHandler &io_handler, bool interactive) override { |
173 | StreamFileSP output_sp(io_handler.GetOutputStreamFileSP()); |
174 | if (output_sp && interactive) { |
175 | output_sp->PutCString( |
176 | cstr: "Enter your debugger command(s). Type 'DONE' to end.\n" ); |
177 | output_sp->Flush(); |
178 | } |
179 | } |
180 | |
181 | void IOHandlerInputComplete(IOHandler &io_handler, |
182 | std::string &line) override { |
183 | io_handler.SetIsDone(true); |
184 | |
185 | // The WatchpointOptions object is owned by the watchpoint or watchpoint |
186 | // location |
187 | WatchpointOptions *wp_options = |
188 | (WatchpointOptions *)io_handler.GetUserData(); |
189 | if (wp_options) { |
190 | std::unique_ptr<WatchpointOptions::CommandData> data_up( |
191 | new WatchpointOptions::CommandData()); |
192 | if (data_up) { |
193 | data_up->user_source.SplitIntoLines(line); |
194 | auto baton_sp = std::make_shared<WatchpointOptions::CommandBaton>( |
195 | std::move(data_up)); |
196 | wp_options->SetCallback(callback: WatchpointOptionsCallbackFunction, baton_sp: baton_sp); |
197 | } |
198 | } |
199 | } |
200 | |
201 | void CollectDataForWatchpointCommandCallback(WatchpointOptions *wp_options, |
202 | CommandReturnObject &result) { |
203 | m_interpreter.GetLLDBCommandsFromIOHandler( |
204 | prompt: "> " , // Prompt |
205 | delegate&: *this, // IOHandlerDelegate |
206 | baton: wp_options); // Baton for the "io_handler" that will be passed back into |
207 | // our IOHandlerDelegate functions |
208 | } |
209 | |
210 | /// Set a one-liner as the callback for the watchpoint. |
211 | void SetWatchpointCommandCallback(WatchpointOptions *wp_options, |
212 | const char *oneliner) { |
213 | std::unique_ptr<WatchpointOptions::CommandData> data_up( |
214 | new WatchpointOptions::CommandData()); |
215 | |
216 | // It's necessary to set both user_source and script_source to the |
217 | // oneliner. The former is used to generate callback description (as in |
218 | // watchpoint command list) while the latter is used for Python to |
219 | // interpret during the actual callback. |
220 | data_up->user_source.AppendString(oneliner); |
221 | data_up->script_source.assign(oneliner); |
222 | data_up->stop_on_error = m_options.m_stop_on_error; |
223 | |
224 | auto baton_sp = |
225 | std::make_shared<WatchpointOptions::CommandBaton>(std::move(data_up)); |
226 | wp_options->SetCallback(callback: WatchpointOptionsCallbackFunction, baton_sp: baton_sp); |
227 | } |
228 | |
229 | static bool |
230 | WatchpointOptionsCallbackFunction(void *baton, |
231 | StoppointCallbackContext *context, |
232 | lldb::user_id_t watch_id) { |
233 | bool ret_value = true; |
234 | if (baton == nullptr) |
235 | return true; |
236 | |
237 | WatchpointOptions::CommandData *data = |
238 | (WatchpointOptions::CommandData *)baton; |
239 | StringList &commands = data->user_source; |
240 | |
241 | if (commands.GetSize() > 0) { |
242 | ExecutionContext exe_ctx(context->exe_ctx_ref); |
243 | Target *target = exe_ctx.GetTargetPtr(); |
244 | if (target) { |
245 | Debugger &debugger = target->GetDebugger(); |
246 | CommandReturnObject result(debugger.GetUseColor()); |
247 | |
248 | // Rig up the results secondary output stream to the debugger's, so the |
249 | // output will come out synchronously if the debugger is set up that |
250 | // way. |
251 | StreamSP output_stream(debugger.GetAsyncOutputStream()); |
252 | StreamSP error_stream(debugger.GetAsyncErrorStream()); |
253 | result.SetImmediateOutputStream(output_stream); |
254 | result.SetImmediateErrorStream(error_stream); |
255 | |
256 | CommandInterpreterRunOptions options; |
257 | options.SetStopOnContinue(true); |
258 | options.SetStopOnError(data->stop_on_error); |
259 | options.SetEchoCommands(false); |
260 | options.SetPrintResults(true); |
261 | options.SetPrintErrors(true); |
262 | options.SetAddToHistory(false); |
263 | |
264 | debugger.GetCommandInterpreter().HandleCommands(commands, context: exe_ctx, |
265 | options, result); |
266 | result.GetImmediateOutputStream()->Flush(); |
267 | result.GetImmediateErrorStream()->Flush(); |
268 | } |
269 | } |
270 | return ret_value; |
271 | } |
272 | |
273 | class CommandOptions : public Options { |
274 | public: |
275 | CommandOptions() = default; |
276 | |
277 | ~CommandOptions() override = default; |
278 | |
279 | Status SetOptionValue(uint32_t option_idx, llvm::StringRef option_arg, |
280 | ExecutionContext *execution_context) override { |
281 | Status error; |
282 | const int short_option = m_getopt_table[option_idx].val; |
283 | |
284 | switch (short_option) { |
285 | case 'o': |
286 | m_use_one_liner = true; |
287 | m_one_liner = std::string(option_arg); |
288 | break; |
289 | |
290 | case 's': |
291 | m_script_language = (lldb::ScriptLanguage)OptionArgParser::ToOptionEnum( |
292 | option_arg, GetDefinitions()[option_idx].enum_values, |
293 | eScriptLanguageNone, error); |
294 | |
295 | switch (m_script_language) { |
296 | case eScriptLanguagePython: |
297 | case eScriptLanguageLua: |
298 | m_use_script_language = true; |
299 | break; |
300 | case eScriptLanguageNone: |
301 | case eScriptLanguageUnknown: |
302 | m_use_script_language = false; |
303 | break; |
304 | } |
305 | break; |
306 | |
307 | case 'e': { |
308 | bool success = false; |
309 | m_stop_on_error = |
310 | OptionArgParser::ToBoolean(s: option_arg, fail_value: false, success_ptr: &success); |
311 | if (!success) |
312 | error.SetErrorStringWithFormat( |
313 | "invalid value for stop-on-error: \"%s\"" , |
314 | option_arg.str().c_str()); |
315 | } break; |
316 | |
317 | case 'F': |
318 | m_use_one_liner = false; |
319 | m_function_name.assign(str: std::string(option_arg)); |
320 | break; |
321 | |
322 | default: |
323 | llvm_unreachable("Unimplemented option" ); |
324 | } |
325 | return error; |
326 | } |
327 | |
328 | void OptionParsingStarting(ExecutionContext *execution_context) override { |
329 | m_use_commands = true; |
330 | m_use_script_language = false; |
331 | m_script_language = eScriptLanguageNone; |
332 | |
333 | m_use_one_liner = false; |
334 | m_stop_on_error = true; |
335 | m_one_liner.clear(); |
336 | m_function_name.clear(); |
337 | } |
338 | |
339 | llvm::ArrayRef<OptionDefinition> GetDefinitions() override { |
340 | return llvm::ArrayRef(g_watchpoint_command_add_options); |
341 | } |
342 | |
343 | // Instance variables to hold the values for command options. |
344 | |
345 | bool m_use_commands = false; |
346 | bool m_use_script_language = false; |
347 | lldb::ScriptLanguage m_script_language = eScriptLanguageNone; |
348 | |
349 | // Instance variables to hold the values for one_liner options. |
350 | bool m_use_one_liner = false; |
351 | std::string m_one_liner; |
352 | bool m_stop_on_error; |
353 | std::string m_function_name; |
354 | }; |
355 | |
356 | protected: |
357 | void DoExecute(Args &command, CommandReturnObject &result) override { |
358 | Target *target = &GetSelectedTarget(); |
359 | |
360 | const WatchpointList &watchpoints = target->GetWatchpointList(); |
361 | size_t num_watchpoints = watchpoints.GetSize(); |
362 | |
363 | if (num_watchpoints == 0) { |
364 | result.AppendError(in_string: "No watchpoints exist to have commands added" ); |
365 | return; |
366 | } |
367 | |
368 | if (!m_options.m_function_name.empty()) { |
369 | if (!m_options.m_use_script_language) { |
370 | m_options.m_script_language = GetDebugger().GetScriptLanguage(); |
371 | m_options.m_use_script_language = true; |
372 | } |
373 | } |
374 | |
375 | std::vector<uint32_t> valid_wp_ids; |
376 | if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, args&: command, |
377 | wp_ids&: valid_wp_ids)) { |
378 | result.AppendError(in_string: "Invalid watchpoints specification." ); |
379 | return; |
380 | } |
381 | |
382 | result.SetStatus(eReturnStatusSuccessFinishNoResult); |
383 | const size_t count = valid_wp_ids.size(); |
384 | for (size_t i = 0; i < count; ++i) { |
385 | uint32_t cur_wp_id = valid_wp_ids.at(i); |
386 | if (cur_wp_id != LLDB_INVALID_WATCH_ID) { |
387 | Watchpoint *wp = target->GetWatchpointList().FindByID(watchID: cur_wp_id).get(); |
388 | // Sanity check wp first. |
389 | if (wp == nullptr) |
390 | continue; |
391 | |
392 | WatchpointOptions *wp_options = wp->GetOptions(); |
393 | // Skip this watchpoint if wp_options is not good. |
394 | if (wp_options == nullptr) |
395 | continue; |
396 | |
397 | // If we are using script language, get the script interpreter in order |
398 | // to set or collect command callback. Otherwise, call the methods |
399 | // associated with this object. |
400 | if (m_options.m_use_script_language) { |
401 | ScriptInterpreter *script_interp = GetDebugger().GetScriptInterpreter( |
402 | /*can_create=*/true, m_options.m_script_language); |
403 | // Special handling for one-liner specified inline. |
404 | if (m_options.m_use_one_liner) { |
405 | script_interp->SetWatchpointCommandCallback( |
406 | wp_options, user_input: m_options.m_one_liner.c_str(), |
407 | /*is_callback=*/false); |
408 | } |
409 | // Special handling for using a Python function by name instead of |
410 | // extending the watchpoint callback data structures, we just |
411 | // automatize what the user would do manually: make their watchpoint |
412 | // command be a function call |
413 | else if (!m_options.m_function_name.empty()) { |
414 | std::string function_signature = m_options.m_function_name; |
415 | function_signature += "(frame, wp, internal_dict)" ; |
416 | script_interp->SetWatchpointCommandCallback( |
417 | wp_options, user_input: function_signature.c_str(), /*is_callback=*/true); |
418 | } else { |
419 | script_interp->CollectDataForWatchpointCommandCallback(wp_options, |
420 | result); |
421 | } |
422 | } else { |
423 | // Special handling for one-liner specified inline. |
424 | if (m_options.m_use_one_liner) |
425 | SetWatchpointCommandCallback(wp_options, |
426 | oneliner: m_options.m_one_liner.c_str()); |
427 | else |
428 | CollectDataForWatchpointCommandCallback(wp_options, result); |
429 | } |
430 | } |
431 | } |
432 | } |
433 | |
434 | private: |
435 | CommandOptions m_options; |
436 | }; |
437 | |
438 | // CommandObjectWatchpointCommandDelete |
439 | |
440 | class CommandObjectWatchpointCommandDelete : public CommandObjectParsed { |
441 | public: |
442 | CommandObjectWatchpointCommandDelete(CommandInterpreter &interpreter) |
443 | : CommandObjectParsed(interpreter, "delete" , |
444 | "Delete the set of commands from a watchpoint." , |
445 | nullptr, eCommandRequiresTarget) { |
446 | AddSimpleArgumentList(arg_type: eArgTypeWatchpointID); |
447 | } |
448 | |
449 | ~CommandObjectWatchpointCommandDelete() override = default; |
450 | |
451 | protected: |
452 | void DoExecute(Args &command, CommandReturnObject &result) override { |
453 | Target *target = &GetSelectedTarget(); |
454 | |
455 | const WatchpointList &watchpoints = target->GetWatchpointList(); |
456 | size_t num_watchpoints = watchpoints.GetSize(); |
457 | |
458 | if (num_watchpoints == 0) { |
459 | result.AppendError(in_string: "No watchpoints exist to have commands deleted" ); |
460 | return; |
461 | } |
462 | |
463 | if (command.GetArgumentCount() == 0) { |
464 | result.AppendError( |
465 | in_string: "No watchpoint specified from which to delete the commands" ); |
466 | return; |
467 | } |
468 | |
469 | std::vector<uint32_t> valid_wp_ids; |
470 | if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, args&: command, |
471 | wp_ids&: valid_wp_ids)) { |
472 | result.AppendError(in_string: "Invalid watchpoints specification." ); |
473 | return; |
474 | } |
475 | |
476 | result.SetStatus(eReturnStatusSuccessFinishNoResult); |
477 | const size_t count = valid_wp_ids.size(); |
478 | for (size_t i = 0; i < count; ++i) { |
479 | uint32_t cur_wp_id = valid_wp_ids.at(n: i); |
480 | if (cur_wp_id != LLDB_INVALID_WATCH_ID) { |
481 | Watchpoint *wp = target->GetWatchpointList().FindByID(watchID: cur_wp_id).get(); |
482 | if (wp) |
483 | wp->ClearCallback(); |
484 | } else { |
485 | result.AppendErrorWithFormat(format: "Invalid watchpoint ID: %u.\n" , cur_wp_id); |
486 | return; |
487 | } |
488 | } |
489 | } |
490 | }; |
491 | |
492 | // CommandObjectWatchpointCommandList |
493 | |
494 | class CommandObjectWatchpointCommandList : public CommandObjectParsed { |
495 | public: |
496 | CommandObjectWatchpointCommandList(CommandInterpreter &interpreter) |
497 | : CommandObjectParsed(interpreter, "list" , |
498 | "List the script or set of commands to be executed " |
499 | "when the watchpoint is hit." , |
500 | nullptr, eCommandRequiresTarget) { |
501 | AddSimpleArgumentList(arg_type: eArgTypeWatchpointID); |
502 | } |
503 | |
504 | ~CommandObjectWatchpointCommandList() override = default; |
505 | |
506 | protected: |
507 | void DoExecute(Args &command, CommandReturnObject &result) override { |
508 | Target *target = &GetSelectedTarget(); |
509 | |
510 | const WatchpointList &watchpoints = target->GetWatchpointList(); |
511 | size_t num_watchpoints = watchpoints.GetSize(); |
512 | |
513 | if (num_watchpoints == 0) { |
514 | result.AppendError(in_string: "No watchpoints exist for which to list commands" ); |
515 | return; |
516 | } |
517 | |
518 | if (command.GetArgumentCount() == 0) { |
519 | result.AppendError( |
520 | in_string: "No watchpoint specified for which to list the commands" ); |
521 | return; |
522 | } |
523 | |
524 | std::vector<uint32_t> valid_wp_ids; |
525 | if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, args&: command, |
526 | wp_ids&: valid_wp_ids)) { |
527 | result.AppendError(in_string: "Invalid watchpoints specification." ); |
528 | return; |
529 | } |
530 | |
531 | result.SetStatus(eReturnStatusSuccessFinishNoResult); |
532 | const size_t count = valid_wp_ids.size(); |
533 | for (size_t i = 0; i < count; ++i) { |
534 | uint32_t cur_wp_id = valid_wp_ids.at(n: i); |
535 | if (cur_wp_id != LLDB_INVALID_WATCH_ID) { |
536 | Watchpoint *wp = target->GetWatchpointList().FindByID(watchID: cur_wp_id).get(); |
537 | |
538 | if (wp) { |
539 | const WatchpointOptions *wp_options = wp->GetOptions(); |
540 | if (wp_options) { |
541 | // Get the callback baton associated with the current watchpoint. |
542 | const Baton *baton = wp_options->GetBaton(); |
543 | if (baton) { |
544 | result.GetOutputStream().Printf(format: "Watchpoint %u:\n" , cur_wp_id); |
545 | baton->GetDescription(s&: result.GetOutputStream().AsRawOstream(), |
546 | level: eDescriptionLevelFull, |
547 | indentation: result.GetOutputStream().GetIndentLevel() + |
548 | 2); |
549 | } else { |
550 | result.AppendMessageWithFormat( |
551 | format: "Watchpoint %u does not have an associated command.\n" , |
552 | cur_wp_id); |
553 | } |
554 | } |
555 | result.SetStatus(eReturnStatusSuccessFinishResult); |
556 | } else { |
557 | result.AppendErrorWithFormat(format: "Invalid watchpoint ID: %u.\n" , |
558 | cur_wp_id); |
559 | } |
560 | } |
561 | } |
562 | } |
563 | }; |
564 | |
565 | // CommandObjectWatchpointCommand |
566 | |
567 | CommandObjectWatchpointCommand::CommandObjectWatchpointCommand( |
568 | CommandInterpreter &interpreter) |
569 | : CommandObjectMultiword( |
570 | interpreter, "command" , |
571 | "Commands for adding, removing and examining LLDB commands " |
572 | "executed when the watchpoint is hit (watchpoint 'commands')." , |
573 | "command <sub-command> [<sub-command-options>] <watchpoint-id>" ) { |
574 | CommandObjectSP add_command_object( |
575 | new CommandObjectWatchpointCommandAdd(interpreter)); |
576 | CommandObjectSP delete_command_object( |
577 | new CommandObjectWatchpointCommandDelete(interpreter)); |
578 | CommandObjectSP list_command_object( |
579 | new CommandObjectWatchpointCommandList(interpreter)); |
580 | |
581 | add_command_object->SetCommandName("watchpoint command add" ); |
582 | delete_command_object->SetCommandName("watchpoint command delete" ); |
583 | list_command_object->SetCommandName("watchpoint command list" ); |
584 | |
585 | LoadSubCommand(cmd_name: "add" , command_obj: add_command_object); |
586 | LoadSubCommand(cmd_name: "delete" , command_obj: delete_command_object); |
587 | LoadSubCommand(cmd_name: "list" , command_obj: list_command_object); |
588 | } |
589 | |
590 | CommandObjectWatchpointCommand::~CommandObjectWatchpointCommand() = default; |
591 | |