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