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