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