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