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 | cstr: "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(callback: WatchpointOptionsCallbackFunction, baton_sp: baton_sp); |
209 | } |
210 | } |
211 | } |
212 | |
213 | void CollectDataForWatchpointCommandCallback(WatchpointOptions *wp_options, |
214 | CommandReturnObject &result) { |
215 | m_interpreter.GetLLDBCommandsFromIOHandler( |
216 | prompt: "> " , // Prompt |
217 | delegate&: *this, // IOHandlerDelegate |
218 | baton: 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(callback: WatchpointOptionsCallbackFunction, baton_sp: 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, context: 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(s: option_arg, fail_value: false, success_ptr: &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(str: 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 | void 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(in_string: "No watchpoints exist to have commands added" ); |
377 | return; |
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, args&: command, |
389 | wp_ids&: valid_wp_ids)) { |
390 | result.AppendError(in_string: "Invalid watchpoints specification." ); |
391 | return; |
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(watchID: 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, user_input: 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, user_input: 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 | oneliner: m_options.m_one_liner.c_str()); |
439 | else |
440 | CollectDataForWatchpointCommandCallback(wp_options, result); |
441 | } |
442 | } |
443 | } |
444 | } |
445 | |
446 | private: |
447 | CommandOptions m_options; |
448 | }; |
449 | |
450 | // CommandObjectWatchpointCommandDelete |
451 | |
452 | class CommandObjectWatchpointCommandDelete : public CommandObjectParsed { |
453 | public: |
454 | CommandObjectWatchpointCommandDelete(CommandInterpreter &interpreter) |
455 | : CommandObjectParsed(interpreter, "delete" , |
456 | "Delete the set of commands from a watchpoint." , |
457 | nullptr, eCommandRequiresTarget) { |
458 | CommandArgumentEntry arg; |
459 | CommandArgumentData wp_id_arg; |
460 | |
461 | // Define the first (and only) variant of this arg. |
462 | wp_id_arg.arg_type = eArgTypeWatchpointID; |
463 | wp_id_arg.arg_repetition = eArgRepeatPlain; |
464 | |
465 | // There is only one variant this argument could be; put it into the |
466 | // argument entry. |
467 | arg.push_back(x: wp_id_arg); |
468 | |
469 | // Push the data for the first argument into the m_arguments vector. |
470 | m_arguments.push_back(x: arg); |
471 | } |
472 | |
473 | ~CommandObjectWatchpointCommandDelete() override = default; |
474 | |
475 | protected: |
476 | void DoExecute(Args &command, CommandReturnObject &result) override { |
477 | Target *target = &GetSelectedTarget(); |
478 | |
479 | const WatchpointList &watchpoints = target->GetWatchpointList(); |
480 | size_t num_watchpoints = watchpoints.GetSize(); |
481 | |
482 | if (num_watchpoints == 0) { |
483 | result.AppendError(in_string: "No watchpoints exist to have commands deleted" ); |
484 | return; |
485 | } |
486 | |
487 | if (command.GetArgumentCount() == 0) { |
488 | result.AppendError( |
489 | in_string: "No watchpoint specified from which to delete the commands" ); |
490 | return; |
491 | } |
492 | |
493 | std::vector<uint32_t> valid_wp_ids; |
494 | if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, args&: command, |
495 | wp_ids&: valid_wp_ids)) { |
496 | result.AppendError(in_string: "Invalid watchpoints specification." ); |
497 | return; |
498 | } |
499 | |
500 | result.SetStatus(eReturnStatusSuccessFinishNoResult); |
501 | const size_t count = valid_wp_ids.size(); |
502 | for (size_t i = 0; i < count; ++i) { |
503 | uint32_t cur_wp_id = valid_wp_ids.at(n: i); |
504 | if (cur_wp_id != LLDB_INVALID_WATCH_ID) { |
505 | Watchpoint *wp = target->GetWatchpointList().FindByID(watchID: cur_wp_id).get(); |
506 | if (wp) |
507 | wp->ClearCallback(); |
508 | } else { |
509 | result.AppendErrorWithFormat(format: "Invalid watchpoint ID: %u.\n" , cur_wp_id); |
510 | return; |
511 | } |
512 | } |
513 | } |
514 | }; |
515 | |
516 | // CommandObjectWatchpointCommandList |
517 | |
518 | class CommandObjectWatchpointCommandList : public CommandObjectParsed { |
519 | public: |
520 | CommandObjectWatchpointCommandList(CommandInterpreter &interpreter) |
521 | : CommandObjectParsed(interpreter, "list" , |
522 | "List the script or set of commands to be executed " |
523 | "when the watchpoint is hit." , |
524 | nullptr, eCommandRequiresTarget) { |
525 | CommandArgumentEntry arg; |
526 | CommandArgumentData wp_id_arg; |
527 | |
528 | // Define the first (and only) variant of this arg. |
529 | wp_id_arg.arg_type = eArgTypeWatchpointID; |
530 | wp_id_arg.arg_repetition = eArgRepeatPlain; |
531 | |
532 | // There is only one variant this argument could be; put it into the |
533 | // argument entry. |
534 | arg.push_back(x: wp_id_arg); |
535 | |
536 | // Push the data for the first argument into the m_arguments vector. |
537 | m_arguments.push_back(x: arg); |
538 | } |
539 | |
540 | ~CommandObjectWatchpointCommandList() override = default; |
541 | |
542 | protected: |
543 | void DoExecute(Args &command, CommandReturnObject &result) override { |
544 | Target *target = &GetSelectedTarget(); |
545 | |
546 | const WatchpointList &watchpoints = target->GetWatchpointList(); |
547 | size_t num_watchpoints = watchpoints.GetSize(); |
548 | |
549 | if (num_watchpoints == 0) { |
550 | result.AppendError(in_string: "No watchpoints exist for which to list commands" ); |
551 | return; |
552 | } |
553 | |
554 | if (command.GetArgumentCount() == 0) { |
555 | result.AppendError( |
556 | in_string: "No watchpoint specified for which to list the commands" ); |
557 | return; |
558 | } |
559 | |
560 | std::vector<uint32_t> valid_wp_ids; |
561 | if (!CommandObjectMultiwordWatchpoint::VerifyWatchpointIDs(target, args&: command, |
562 | wp_ids&: valid_wp_ids)) { |
563 | result.AppendError(in_string: "Invalid watchpoints specification." ); |
564 | return; |
565 | } |
566 | |
567 | result.SetStatus(eReturnStatusSuccessFinishNoResult); |
568 | const size_t count = valid_wp_ids.size(); |
569 | for (size_t i = 0; i < count; ++i) { |
570 | uint32_t cur_wp_id = valid_wp_ids.at(n: i); |
571 | if (cur_wp_id != LLDB_INVALID_WATCH_ID) { |
572 | Watchpoint *wp = target->GetWatchpointList().FindByID(watchID: cur_wp_id).get(); |
573 | |
574 | if (wp) { |
575 | const WatchpointOptions *wp_options = wp->GetOptions(); |
576 | if (wp_options) { |
577 | // Get the callback baton associated with the current watchpoint. |
578 | const Baton *baton = wp_options->GetBaton(); |
579 | if (baton) { |
580 | result.GetOutputStream().Printf(format: "Watchpoint %u:\n" , cur_wp_id); |
581 | baton->GetDescription(s&: result.GetOutputStream().AsRawOstream(), |
582 | level: eDescriptionLevelFull, |
583 | indentation: result.GetOutputStream().GetIndentLevel() + |
584 | 2); |
585 | } else { |
586 | result.AppendMessageWithFormat( |
587 | format: "Watchpoint %u does not have an associated command.\n" , |
588 | cur_wp_id); |
589 | } |
590 | } |
591 | result.SetStatus(eReturnStatusSuccessFinishResult); |
592 | } else { |
593 | result.AppendErrorWithFormat(format: "Invalid watchpoint ID: %u.\n" , |
594 | cur_wp_id); |
595 | } |
596 | } |
597 | } |
598 | } |
599 | }; |
600 | |
601 | // CommandObjectWatchpointCommand |
602 | |
603 | CommandObjectWatchpointCommand::CommandObjectWatchpointCommand( |
604 | CommandInterpreter &interpreter) |
605 | : CommandObjectMultiword( |
606 | interpreter, "command" , |
607 | "Commands for adding, removing and examining LLDB commands " |
608 | "executed when the watchpoint is hit (watchpoint 'commands')." , |
609 | "command <sub-command> [<sub-command-options>] <watchpoint-id>" ) { |
610 | CommandObjectSP add_command_object( |
611 | new CommandObjectWatchpointCommandAdd(interpreter)); |
612 | CommandObjectSP delete_command_object( |
613 | new CommandObjectWatchpointCommandDelete(interpreter)); |
614 | CommandObjectSP list_command_object( |
615 | new CommandObjectWatchpointCommandList(interpreter)); |
616 | |
617 | add_command_object->SetCommandName("watchpoint command add" ); |
618 | delete_command_object->SetCommandName("watchpoint command delete" ); |
619 | list_command_object->SetCommandName("watchpoint command list" ); |
620 | |
621 | LoadSubCommand(cmd_name: "add" , command_obj: add_command_object); |
622 | LoadSubCommand(cmd_name: "delete" , command_obj: delete_command_object); |
623 | LoadSubCommand(cmd_name: "list" , command_obj: list_command_object); |
624 | } |
625 | |
626 | CommandObjectWatchpointCommand::~CommandObjectWatchpointCommand() = default; |
627 | |