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