xref: /llvm-project/lldb/source/Commands/CommandObjectExpression.cpp (revision 1124045ac739480b69eb7c9be2ce09d4898c4ed0)
1 //===-- CommandObjectExpression.cpp -----------------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "CommandObjectExpression.h"
11 
12 // C Includes
13 // C++ Includes
14 // Other libraries and framework includes
15 // Project includes
16 #include "lldb/Core/Value.h"
17 #include "lldb/Core/ValueObjectVariable.h"
18 #include "lldb/DataFormatters/ValueObjectPrinter.h"
19 #include "lldb/Expression/ClangExpressionVariable.h"
20 #include "lldb/Expression/ClangUserExpression.h"
21 #include "lldb/Expression/ClangFunction.h"
22 #include "lldb/Expression/DWARFExpression.h"
23 #include "lldb/Host/Host.h"
24 #include "lldb/Host/StringConvert.h"
25 #include "lldb/Core/Debugger.h"
26 #include "lldb/Interpreter/CommandInterpreter.h"
27 #include "lldb/Interpreter/CommandReturnObject.h"
28 #include "lldb/Target/ObjCLanguageRuntime.h"
29 #include "lldb/Symbol/ObjectFile.h"
30 #include "lldb/Symbol/Variable.h"
31 #include "lldb/Target/Process.h"
32 #include "lldb/Target/StackFrame.h"
33 #include "lldb/Target/Target.h"
34 #include "lldb/Target/Thread.h"
35 #include "llvm/ADT/STLExtras.h"
36 #include "llvm/ADT/StringRef.h"
37 
38 using namespace lldb;
39 using namespace lldb_private;
40 
41 CommandObjectExpression::CommandOptions::CommandOptions () :
42     OptionGroup()
43 {
44 }
45 
46 
47 CommandObjectExpression::CommandOptions::~CommandOptions ()
48 {
49 }
50 
51 static OptionEnumValueElement g_description_verbosity_type[] =
52 {
53     { eLanguageRuntimeDescriptionDisplayVerbosityCompact,      "compact",       "Only show the description string"},
54     { eLanguageRuntimeDescriptionDisplayVerbosityFull,         "full",          "Show the full output, including persistent variable's name and type"},
55     { 0, NULL, NULL }
56 };
57 
58 OptionDefinition
59 CommandObjectExpression::CommandOptions::g_option_table[] =
60 {
61     { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "all-threads",        'a', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeBoolean,    "Should we run all threads if the execution doesn't complete on one thread."},
62     { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "ignore-breakpoints", 'i', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeBoolean,    "Ignore breakpoint hits while running expressions"},
63     { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "timeout",            't', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeUnsignedInteger,  "Timeout value (in microseconds) for running the expression."},
64     { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "unwind-on-error",    'u', OptionParser::eRequiredArgument, NULL, NULL, 0, eArgTypeBoolean,    "Clean up program state if the expression causes a crash, or raises a signal.  Note, unlike gdb hitting a breakpoint is controlled by another option (-i)."},
65     { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "debug",              'g', OptionParser::eNoArgument      , NULL, NULL, 0, eArgTypeNone,       "When specified, debug the JIT code by setting a breakpoint on the first instruction and forcing breakpoints to not be ignored (-i0) and no unwinding to happen on error (-u0)."},
66     { LLDB_OPT_SET_1, false, "description-verbosity", 'v', OptionParser::eOptionalArgument, NULL, g_description_verbosity_type, 0, eArgTypeDescriptionVerbosity,        "How verbose should the output of this expression be, if the object description is asked for."},
67 };
68 
69 
70 uint32_t
71 CommandObjectExpression::CommandOptions::GetNumDefinitions ()
72 {
73     return llvm::array_lengthof(g_option_table);
74 }
75 
76 Error
77 CommandObjectExpression::CommandOptions::SetOptionValue (CommandInterpreter &interpreter,
78                                                          uint32_t option_idx,
79                                                          const char *option_arg)
80 {
81     Error error;
82 
83     const int short_option = g_option_table[option_idx].short_option;
84 
85     switch (short_option)
86     {
87       //case 'l':
88       //if (language.SetLanguageFromCString (option_arg) == false)
89       //{
90       //    error.SetErrorStringWithFormat("invalid language option argument '%s'", option_arg);
91       //}
92       //break;
93 
94     case 'a':
95         {
96             bool success;
97             bool result;
98             result = Args::StringToBoolean(option_arg, true, &success);
99             if (!success)
100                 error.SetErrorStringWithFormat("invalid all-threads value setting: \"%s\"", option_arg);
101             else
102                 try_all_threads = result;
103         }
104         break;
105 
106     case 'i':
107         {
108             bool success;
109             bool tmp_value = Args::StringToBoolean(option_arg, true, &success);
110             if (success)
111                 ignore_breakpoints = tmp_value;
112             else
113                 error.SetErrorStringWithFormat("could not convert \"%s\" to a boolean value.", option_arg);
114             break;
115         }
116     case 't':
117         {
118             bool success;
119             uint32_t result;
120             result = StringConvert::ToUInt32(option_arg, 0, 0, &success);
121             if (success)
122                 timeout = result;
123             else
124                 error.SetErrorStringWithFormat ("invalid timeout setting \"%s\"", option_arg);
125         }
126         break;
127 
128     case 'u':
129         {
130             bool success;
131             bool tmp_value = Args::StringToBoolean(option_arg, true, &success);
132             if (success)
133                 unwind_on_error = tmp_value;
134             else
135                 error.SetErrorStringWithFormat("could not convert \"%s\" to a boolean value.", option_arg);
136             break;
137         }
138 
139     case 'v':
140         if (!option_arg)
141         {
142             m_verbosity = eLanguageRuntimeDescriptionDisplayVerbosityFull;
143             break;
144         }
145         m_verbosity = (LanguageRuntimeDescriptionDisplayVerbosity) Args::StringToOptionEnum(option_arg, g_option_table[option_idx].enum_values, 0, error);
146         if (!error.Success())
147             error.SetErrorStringWithFormat ("unrecognized value for description-verbosity '%s'", option_arg);
148         break;
149 
150     case 'g':
151         debug = true;
152         unwind_on_error = false;
153         ignore_breakpoints = false;
154         break;
155 
156     default:
157         error.SetErrorStringWithFormat("invalid short option character '%c'", short_option);
158         break;
159     }
160 
161     return error;
162 }
163 
164 void
165 CommandObjectExpression::CommandOptions::OptionParsingStarting (CommandInterpreter &interpreter)
166 {
167     Process *process = interpreter.GetExecutionContext().GetProcessPtr();
168     if (process != NULL)
169     {
170         ignore_breakpoints = process->GetIgnoreBreakpointsInExpressions();
171         unwind_on_error    = process->GetUnwindOnErrorInExpressions();
172     }
173     else
174     {
175         ignore_breakpoints = true;
176         unwind_on_error = true;
177     }
178 
179     show_summary = true;
180     try_all_threads = true;
181     timeout = 0;
182     debug = false;
183     m_verbosity = eLanguageRuntimeDescriptionDisplayVerbosityCompact;
184 }
185 
186 const OptionDefinition*
187 CommandObjectExpression::CommandOptions::GetDefinitions ()
188 {
189     return g_option_table;
190 }
191 
192 CommandObjectExpression::CommandObjectExpression (CommandInterpreter &interpreter) :
193     CommandObjectRaw (interpreter,
194                       "expression",
195                       "Evaluate a C/ObjC/C++ expression in the current program context, using user defined variables and variables currently in scope.",
196                       NULL,
197                       eCommandProcessMustBePaused | eCommandTryTargetAPILock),
198     IOHandlerDelegate (IOHandlerDelegate::Completion::Expression),
199     m_option_group (interpreter),
200     m_format_options (eFormatDefault),
201     m_command_options (),
202     m_expr_line_count (0),
203     m_expr_lines ()
204 {
205   SetHelpLong(
206 "Timeouts:\n\
207     If the expression can be evaluated statically (without running code) then it will be.\n\
208     Otherwise, by default the expression will run on the current thread with a short timeout:\n\
209     currently .25 seconds.  If it doesn't return in that time, the evaluation will be interrupted\n\
210     and resumed with all threads running.  You can use the -a option to disable retrying on all\n\
211     threads.  You can use the -t option to set a shorter timeout.\n\
212 \n\
213 User defined variables:\n\
214     You can define your own variables for convenience or to be used in subsequent expressions.\n\
215     You define them the same way you would define variables in C.  If the first character of \n\
216     your user defined variable is a $, then the variable's value will be available in future\n\
217     expressions, otherwise it will just be available in the current expression.\n\
218 \n\
219 \n\
220 Continuing evaluation after a breakpoint:\n\
221     If the \"-i false\" option is used, and execution is interrupted by a breakpoint hit, once\n\
222     you are done with your investigation, you can either remove the expression execution frames\n\
223     from the stack with \"thread return -x\" or if you are still interested in the expression result\n\
224     you can issue the \"continue\" command and the expression evaluation will complete and the\n\
225     expression result will be available using the \"thread.completed-expression\" key in the thread\n\
226     format.\n\
227 \n\
228 Examples: \n\
229 \n\
230    expr my_struct->a = my_array[3] \n\
231    expr -f bin -- (index * 8) + 5 \n\
232    expr unsigned int $foo = 5\n\
233    expr char c[] = \"foo\"; c[0]\n");
234 
235     CommandArgumentEntry arg;
236     CommandArgumentData expression_arg;
237 
238     // Define the first (and only) variant of this arg.
239     expression_arg.arg_type = eArgTypeExpression;
240     expression_arg.arg_repetition = eArgRepeatPlain;
241 
242     // There is only one variant this argument could be; put it into the argument entry.
243     arg.push_back (expression_arg);
244 
245     // Push the data for the first argument into the m_arguments vector.
246     m_arguments.push_back (arg);
247 
248     // Add the "--format" and "--gdb-format"
249     m_option_group.Append (&m_format_options, OptionGroupFormat::OPTION_GROUP_FORMAT | OptionGroupFormat::OPTION_GROUP_GDB_FMT, LLDB_OPT_SET_1);
250     m_option_group.Append (&m_command_options);
251     m_option_group.Append (&m_varobj_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1 | LLDB_OPT_SET_2);
252     m_option_group.Finalize();
253 }
254 
255 CommandObjectExpression::~CommandObjectExpression ()
256 {
257 }
258 
259 Options *
260 CommandObjectExpression::GetOptions ()
261 {
262     return &m_option_group;
263 }
264 
265 bool
266 CommandObjectExpression::EvaluateExpression
267 (
268     const char *expr,
269     Stream *output_stream,
270     Stream *error_stream,
271     CommandReturnObject *result
272 )
273 {
274     // Don't use m_exe_ctx as this might be called asynchronously
275     // after the command object DoExecute has finished when doing
276     // multi-line expression that use an input reader...
277     ExecutionContext exe_ctx (m_interpreter.GetExecutionContext());
278 
279     Target *target = exe_ctx.GetTargetPtr();
280 
281     if (!target)
282         target = GetDummyTarget();
283 
284     if (target)
285     {
286         lldb::ValueObjectSP result_valobj_sp;
287 
288         bool keep_in_memory = true;
289 
290         EvaluateExpressionOptions options;
291         options.SetCoerceToId(m_varobj_options.use_objc);
292         options.SetUnwindOnError(m_command_options.unwind_on_error);
293         options.SetIgnoreBreakpoints (m_command_options.ignore_breakpoints);
294         options.SetKeepInMemory(keep_in_memory);
295         options.SetUseDynamic(m_varobj_options.use_dynamic);
296         options.SetTryAllThreads(m_command_options.try_all_threads);
297         options.SetDebug(m_command_options.debug);
298 
299         // If there is any chance we are going to stop and want to see
300         // what went wrong with our expression, we should generate debug info
301         if (!m_command_options.ignore_breakpoints ||
302             !m_command_options.unwind_on_error)
303             options.SetGenerateDebugInfo(true);
304 
305         if (m_command_options.timeout > 0)
306             options.SetTimeoutUsec(m_command_options.timeout);
307         else
308             options.SetTimeoutUsec(0);
309 
310         target->EvaluateExpression(expr, exe_ctx.GetFramePtr(),
311                                    result_valobj_sp, options);
312 
313         if (result_valobj_sp)
314         {
315             Format format = m_format_options.GetFormat();
316 
317             if (result_valobj_sp->GetError().Success())
318             {
319                 if (format != eFormatVoid)
320                 {
321                     if (format != eFormatDefault)
322                         result_valobj_sp->SetFormat (format);
323 
324                     DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions(m_command_options.m_verbosity,format));
325 
326                     result_valobj_sp->Dump(*output_stream,options);
327 
328                     if (result)
329                         result->SetStatus (eReturnStatusSuccessFinishResult);
330                 }
331             }
332             else
333             {
334                 if (result_valobj_sp->GetError().GetError() == ClangUserExpression::kNoResult)
335                 {
336                     if (format != eFormatVoid && m_interpreter.GetDebugger().GetNotifyVoid())
337                     {
338                         error_stream->PutCString("(void)\n");
339                     }
340 
341                     if (result)
342                         result->SetStatus (eReturnStatusSuccessFinishResult);
343                 }
344                 else
345                 {
346                     const char *error_cstr = result_valobj_sp->GetError().AsCString();
347                     if (error_cstr && error_cstr[0])
348                     {
349                         const size_t error_cstr_len = strlen (error_cstr);
350                         const bool ends_with_newline = error_cstr[error_cstr_len - 1] == '\n';
351                         if (strstr(error_cstr, "error:") != error_cstr)
352                             error_stream->PutCString ("error: ");
353                         error_stream->Write(error_cstr, error_cstr_len);
354                         if (!ends_with_newline)
355                             error_stream->EOL();
356                     }
357                     else
358                     {
359                         error_stream->PutCString ("error: unknown error\n");
360                     }
361 
362                     if (result)
363                         result->SetStatus (eReturnStatusFailed);
364                 }
365             }
366         }
367     }
368     else
369     {
370         error_stream->Printf ("error: invalid execution context for expression\n");
371         return false;
372     }
373 
374     return true;
375 }
376 
377 void
378 CommandObjectExpression::IOHandlerInputComplete (IOHandler &io_handler, std::string &line)
379 {
380     io_handler.SetIsDone(true);
381 //    StreamSP output_stream = io_handler.GetDebugger().GetAsyncOutputStream();
382 //    StreamSP error_stream = io_handler.GetDebugger().GetAsyncErrorStream();
383     StreamFileSP output_sp(io_handler.GetOutputStreamFile());
384     StreamFileSP error_sp(io_handler.GetErrorStreamFile());
385 
386     EvaluateExpression (line.c_str(),
387                         output_sp.get(),
388                         error_sp.get());
389     if (output_sp)
390         output_sp->Flush();
391     if (error_sp)
392         error_sp->Flush();
393 }
394 
395 LineStatus
396 CommandObjectExpression::IOHandlerLinesUpdated (IOHandler &io_handler,
397                                                 StringList &lines,
398                                                 uint32_t line_idx,
399                                                 Error &error)
400 {
401     if (line_idx == UINT32_MAX)
402     {
403         // Remove the last line from "lines" so it doesn't appear
404         // in our final expression
405         lines.PopBack();
406         error.Clear();
407         return LineStatus::Done;
408     }
409     else if (line_idx + 1 == lines.GetSize())
410     {
411         // The last line was edited, if this line is empty, then we are done
412         // getting our multiple lines.
413         if (lines[line_idx].empty())
414             return LineStatus::Done;
415     }
416     return LineStatus::Success;
417 }
418 
419 void
420 CommandObjectExpression::GetMultilineExpression ()
421 {
422     m_expr_lines.clear();
423     m_expr_line_count = 0;
424 
425     Debugger &debugger = GetCommandInterpreter().GetDebugger();
426     bool color_prompt = debugger.GetUseColor();
427     const bool multiple_lines = true; // Get multiple lines
428     IOHandlerSP io_handler_sp (new IOHandlerEditline (debugger,
429                                                       IOHandler::Type::Expression,
430                                                       "lldb-expr",      // Name of input reader for history
431                                                       NULL,             // No prompt
432                                                       NULL,             // Continuation prompt
433                                                       multiple_lines,
434                                                       color_prompt,
435                                                       1,                // Show line numbers starting at 1
436                                                       *this));
437 
438     StreamFileSP output_sp(io_handler_sp->GetOutputStreamFile());
439     if (output_sp)
440     {
441         output_sp->PutCString("Enter expressions, then terminate with an empty line to evaluate:\n");
442         output_sp->Flush();
443     }
444     debugger.PushIOHandler(io_handler_sp);
445 }
446 
447 bool
448 CommandObjectExpression::DoExecute
449 (
450     const char *command,
451     CommandReturnObject &result
452 )
453 {
454     m_option_group.NotifyOptionParsingStarting();
455 
456     const char * expr = NULL;
457 
458     if (command[0] == '\0')
459     {
460         GetMultilineExpression ();
461         return result.Succeeded();
462     }
463 
464     if (command[0] == '-')
465     {
466         // We have some options and these options MUST end with --.
467         const char *end_options = NULL;
468         const char *s = command;
469         while (s && s[0])
470         {
471             end_options = ::strstr (s, "--");
472             if (end_options)
473             {
474                 end_options += 2; // Get past the "--"
475                 if (::isspace (end_options[0]))
476                 {
477                     expr = end_options;
478                     while (::isspace (*expr))
479                         ++expr;
480                     break;
481                 }
482             }
483             s = end_options;
484         }
485 
486         if (end_options)
487         {
488             Args args (llvm::StringRef(command, end_options - command));
489             if (!ParseOptions (args, result))
490                 return false;
491 
492             Error error (m_option_group.NotifyOptionParsingFinished());
493             if (error.Fail())
494             {
495                 result.AppendError (error.AsCString());
496                 result.SetStatus (eReturnStatusFailed);
497                 return false;
498             }
499 
500             // No expression following options
501             if (expr == NULL || expr[0] == '\0')
502             {
503                 GetMultilineExpression ();
504                 return result.Succeeded();
505             }
506         }
507     }
508 
509     if (expr == NULL)
510         expr = command;
511 
512     if (EvaluateExpression (expr, &(result.GetOutputStream()), &(result.GetErrorStream()), &result))
513         return true;
514 
515     result.SetStatus (eReturnStatusFailed);
516     return false;
517 }
518 
519