|  | //===-- CommandObjectExpression.cpp -----------------------------*- C++ -*-===// | 
|  | // | 
|  | //                     The LLVM Compiler Infrastructure | 
|  | // | 
|  | // This file is distributed under the University of Illinois Open Source | 
|  | // License. See LICENSE.TXT for details. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "lldb/lldb-python.h" | 
|  |  | 
|  | #include "CommandObjectExpression.h" | 
|  |  | 
|  | // C Includes | 
|  | // C++ Includes | 
|  | // Other libraries and framework includes | 
|  | // Project includes | 
|  | #include "lldb/Interpreter/Args.h" | 
|  | #include "lldb/Core/Value.h" | 
|  | #include "lldb/Core/ValueObjectVariable.h" | 
|  | #include "lldb/DataFormatters/ValueObjectPrinter.h" | 
|  | #include "lldb/Expression/ClangExpressionVariable.h" | 
|  | #include "lldb/Expression/ClangUserExpression.h" | 
|  | #include "lldb/Expression/ClangFunction.h" | 
|  | #include "lldb/Expression/DWARFExpression.h" | 
|  | #include "lldb/Host/Host.h" | 
|  | #include "lldb/Core/Debugger.h" | 
|  | #include "lldb/Interpreter/CommandInterpreter.h" | 
|  | #include "lldb/Interpreter/CommandReturnObject.h" | 
|  | #include "lldb/Target/ObjCLanguageRuntime.h" | 
|  | #include "lldb/Symbol/ObjectFile.h" | 
|  | #include "lldb/Symbol/Variable.h" | 
|  | #include "lldb/Target/Process.h" | 
|  | #include "lldb/Target/StackFrame.h" | 
|  | #include "lldb/Target/Target.h" | 
|  | #include "lldb/Target/Thread.h" | 
|  | #include "llvm/ADT/StringRef.h" | 
|  |  | 
|  | using namespace lldb; | 
|  | using namespace lldb_private; | 
|  |  | 
|  | CommandObjectExpression::CommandOptions::CommandOptions () : | 
|  | OptionGroup() | 
|  | { | 
|  | } | 
|  |  | 
|  |  | 
|  | CommandObjectExpression::CommandOptions::~CommandOptions () | 
|  | { | 
|  | } | 
|  |  | 
|  | static OptionEnumValueElement g_description_verbosity_type[] = | 
|  | { | 
|  | { eLanguageRuntimeDescriptionDisplayVerbosityCompact,      "compact",       "Only show the description string"}, | 
|  | { eLanguageRuntimeDescriptionDisplayVerbosityFull,         "full",          "Show the full output, including persistent variable's name and type"}, | 
|  | { 0, NULL, NULL } | 
|  | }; | 
|  |  | 
|  | OptionDefinition | 
|  | CommandObjectExpression::CommandOptions::g_option_table[] = | 
|  | { | 
|  | { 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."}, | 
|  | { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "ignore-breakpoints", 'i', OptionParser::eRequiredArgument, NULL, 0, eArgTypeBoolean,    "Ignore breakpoint hits while running expressions"}, | 
|  | { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "timeout",            't', OptionParser::eRequiredArgument, NULL, 0, eArgTypeUnsignedInteger,  "Timeout value (in microseconds) for running the expression."}, | 
|  | { 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)."}, | 
|  | { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "debug",              'g', OptionParser::eNoArgument      , 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)."}, | 
|  | { 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."}, | 
|  | }; | 
|  |  | 
|  |  | 
|  | uint32_t | 
|  | CommandObjectExpression::CommandOptions::GetNumDefinitions () | 
|  | { | 
|  | return sizeof(g_option_table)/sizeof(OptionDefinition); | 
|  | } | 
|  |  | 
|  | Error | 
|  | CommandObjectExpression::CommandOptions::SetOptionValue (CommandInterpreter &interpreter, | 
|  | uint32_t option_idx, | 
|  | const char *option_arg) | 
|  | { | 
|  | Error error; | 
|  |  | 
|  | const int short_option = g_option_table[option_idx].short_option; | 
|  |  | 
|  | switch (short_option) | 
|  | { | 
|  | //case 'l': | 
|  | //if (language.SetLanguageFromCString (option_arg) == false) | 
|  | //{ | 
|  | //    error.SetErrorStringWithFormat("invalid language option argument '%s'", option_arg); | 
|  | //} | 
|  | //break; | 
|  |  | 
|  | case 'a': | 
|  | { | 
|  | bool success; | 
|  | bool result; | 
|  | result = Args::StringToBoolean(option_arg, true, &success); | 
|  | if (!success) | 
|  | error.SetErrorStringWithFormat("invalid all-threads value setting: \"%s\"", option_arg); | 
|  | else | 
|  | try_all_threads = result; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case 'i': | 
|  | { | 
|  | bool success; | 
|  | bool tmp_value = Args::StringToBoolean(option_arg, true, &success); | 
|  | if (success) | 
|  | ignore_breakpoints = tmp_value; | 
|  | else | 
|  | error.SetErrorStringWithFormat("could not convert \"%s\" to a boolean value.", option_arg); | 
|  | break; | 
|  | } | 
|  | case 't': | 
|  | { | 
|  | bool success; | 
|  | uint32_t result; | 
|  | result = Args::StringToUInt32(option_arg, 0, 0, &success); | 
|  | if (success) | 
|  | timeout = result; | 
|  | else | 
|  | error.SetErrorStringWithFormat ("invalid timeout setting \"%s\"", option_arg); | 
|  | } | 
|  | break; | 
|  |  | 
|  | case 'u': | 
|  | { | 
|  | bool success; | 
|  | bool tmp_value = Args::StringToBoolean(option_arg, true, &success); | 
|  | if (success) | 
|  | unwind_on_error = tmp_value; | 
|  | else | 
|  | error.SetErrorStringWithFormat("could not convert \"%s\" to a boolean value.", option_arg); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case 'v': | 
|  | if (!option_arg) | 
|  | { | 
|  | m_verbosity = eLanguageRuntimeDescriptionDisplayVerbosityFull; | 
|  | break; | 
|  | } | 
|  | m_verbosity = (LanguageRuntimeDescriptionDisplayVerbosity) Args::StringToOptionEnum(option_arg, g_option_table[option_idx].enum_values, 0, error); | 
|  | if (!error.Success()) | 
|  | error.SetErrorStringWithFormat ("unrecognized value for description-verbosity '%s'", option_arg); | 
|  | break; | 
|  |  | 
|  | case 'g': | 
|  | debug = true; | 
|  | unwind_on_error = false; | 
|  | ignore_breakpoints = false; | 
|  | break; | 
|  |  | 
|  | default: | 
|  | error.SetErrorStringWithFormat("invalid short option character '%c'", short_option); | 
|  | break; | 
|  | } | 
|  |  | 
|  | return error; | 
|  | } | 
|  |  | 
|  | void | 
|  | CommandObjectExpression::CommandOptions::OptionParsingStarting (CommandInterpreter &interpreter) | 
|  | { | 
|  | Process *process = interpreter.GetExecutionContext().GetProcessPtr(); | 
|  | if (process != NULL) | 
|  | { | 
|  | ignore_breakpoints = process->GetIgnoreBreakpointsInExpressions(); | 
|  | unwind_on_error    = process->GetUnwindOnErrorInExpressions(); | 
|  | } | 
|  | else | 
|  | { | 
|  | ignore_breakpoints = true; | 
|  | unwind_on_error = true; | 
|  | } | 
|  |  | 
|  | show_summary = true; | 
|  | try_all_threads = true; | 
|  | timeout = 0; | 
|  | debug = false; | 
|  | m_verbosity = eLanguageRuntimeDescriptionDisplayVerbosityCompact; | 
|  | } | 
|  |  | 
|  | const OptionDefinition* | 
|  | CommandObjectExpression::CommandOptions::GetDefinitions () | 
|  | { | 
|  | return g_option_table; | 
|  | } | 
|  |  | 
|  | CommandObjectExpression::CommandObjectExpression (CommandInterpreter &interpreter) : | 
|  | CommandObjectRaw (interpreter, | 
|  | "expression", | 
|  | "Evaluate a C/ObjC/C++ expression in the current program context, using user defined variables and variables currently in scope.", | 
|  | NULL, | 
|  | eFlagProcessMustBePaused | eFlagTryTargetAPILock), | 
|  | IOHandlerDelegate (IOHandlerDelegate::Completion::Expression), | 
|  | m_option_group (interpreter), | 
|  | m_format_options (eFormatDefault), | 
|  | m_command_options (), | 
|  | m_expr_line_count (0), | 
|  | m_expr_lines () | 
|  | { | 
|  | SetHelpLong( | 
|  | "Timeouts:\n\ | 
|  | If the expression can be evaluated statically (without runnning code) then it will be.\n\ | 
|  | Otherwise, by default the expression will run on the current thread with a short timeout:\n\ | 
|  | currently .25 seconds.  If it doesn't return in that time, the evaluation will be interrupted\n\ | 
|  | and resumed with all threads running.  You can use the -a option to disable retrying on all\n\ | 
|  | threads.  You can use the -t option to set a shorter timeout.\n\ | 
|  | \n\ | 
|  | User defined variables:\n\ | 
|  | You can define your own variables for convenience or to be used in subsequent expressions.\n\ | 
|  | You define them the same way you would define variables in C.  If the first character of \n\ | 
|  | your user defined variable is a $, then the variable's value will be available in future\n\ | 
|  | expressions, otherwise it will just be available in the current expression.\n\ | 
|  | \n\ | 
|  | Examples: \n\ | 
|  | \n\ | 
|  | expr my_struct->a = my_array[3] \n\ | 
|  | expr -f bin -- (index * 8) + 5 \n\ | 
|  | expr unsigned int $foo = 5\n\ | 
|  | expr char c[] = \"foo\"; c[0]\n"); | 
|  |  | 
|  | CommandArgumentEntry arg; | 
|  | CommandArgumentData expression_arg; | 
|  |  | 
|  | // Define the first (and only) variant of this arg. | 
|  | expression_arg.arg_type = eArgTypeExpression; | 
|  | expression_arg.arg_repetition = eArgRepeatPlain; | 
|  |  | 
|  | // There is only one variant this argument could be; put it into the argument entry. | 
|  | arg.push_back (expression_arg); | 
|  |  | 
|  | // Push the data for the first argument into the m_arguments vector. | 
|  | m_arguments.push_back (arg); | 
|  |  | 
|  | // Add the "--format" and "--gdb-format" | 
|  | m_option_group.Append (&m_format_options, OptionGroupFormat::OPTION_GROUP_FORMAT | OptionGroupFormat::OPTION_GROUP_GDB_FMT, LLDB_OPT_SET_1); | 
|  | m_option_group.Append (&m_command_options); | 
|  | m_option_group.Append (&m_varobj_options, LLDB_OPT_SET_ALL, LLDB_OPT_SET_1 | LLDB_OPT_SET_2); | 
|  | m_option_group.Finalize(); | 
|  | } | 
|  |  | 
|  | CommandObjectExpression::~CommandObjectExpression () | 
|  | { | 
|  | } | 
|  |  | 
|  | Options * | 
|  | CommandObjectExpression::GetOptions () | 
|  | { | 
|  | return &m_option_group; | 
|  | } | 
|  |  | 
|  | bool | 
|  | CommandObjectExpression::EvaluateExpression | 
|  | ( | 
|  | const char *expr, | 
|  | Stream *output_stream, | 
|  | Stream *error_stream, | 
|  | CommandReturnObject *result | 
|  | ) | 
|  | { | 
|  | // Don't use m_exe_ctx as this might be called asynchronously | 
|  | // after the command object DoExecute has finished when doing | 
|  | // multi-line expression that use an input reader... | 
|  | ExecutionContext exe_ctx (m_interpreter.GetExecutionContext()); | 
|  |  | 
|  | Target *target = exe_ctx.GetTargetPtr(); | 
|  |  | 
|  | if (!target) | 
|  | target = Host::GetDummyTarget(m_interpreter.GetDebugger()).get(); | 
|  |  | 
|  | if (target) | 
|  | { | 
|  | lldb::ValueObjectSP result_valobj_sp; | 
|  |  | 
|  | bool keep_in_memory = true; | 
|  |  | 
|  | EvaluateExpressionOptions options; | 
|  | options.SetCoerceToId(m_varobj_options.use_objc); | 
|  | options.SetUnwindOnError(m_command_options.unwind_on_error); | 
|  | options.SetIgnoreBreakpoints (m_command_options.ignore_breakpoints); | 
|  | options.SetKeepInMemory(keep_in_memory); | 
|  | options.SetUseDynamic(m_varobj_options.use_dynamic); | 
|  | options.SetTryAllThreads(m_command_options.try_all_threads); | 
|  | options.SetDebug(m_command_options.debug); | 
|  |  | 
|  | // If there is any chance we are going to stop and want to see | 
|  | // what went wrong with our expression, we should generate debug info | 
|  | if (!m_command_options.ignore_breakpoints || | 
|  | !m_command_options.unwind_on_error) | 
|  | options.SetGenerateDebugInfo(true); | 
|  |  | 
|  | if (m_command_options.timeout > 0) | 
|  | options.SetTimeoutUsec(m_command_options.timeout); | 
|  | else | 
|  | options.SetTimeoutUsec(0); | 
|  |  | 
|  | target->EvaluateExpression(expr, exe_ctx.GetFramePtr(), | 
|  | result_valobj_sp, options); | 
|  |  | 
|  | if (result_valobj_sp) | 
|  | { | 
|  | Format format = m_format_options.GetFormat(); | 
|  |  | 
|  | if (result_valobj_sp->GetError().Success()) | 
|  | { | 
|  | if (format != eFormatVoid) | 
|  | { | 
|  | if (format != eFormatDefault) | 
|  | result_valobj_sp->SetFormat (format); | 
|  |  | 
|  | DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions(m_command_options.m_verbosity,format)); | 
|  |  | 
|  | result_valobj_sp->Dump(*output_stream,options); | 
|  |  | 
|  | if (result) | 
|  | result->SetStatus (eReturnStatusSuccessFinishResult); | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | if (result_valobj_sp->GetError().GetError() == ClangUserExpression::kNoResult) | 
|  | { | 
|  | if (format != eFormatVoid && m_interpreter.GetDebugger().GetNotifyVoid()) | 
|  | { | 
|  | error_stream->PutCString("(void)\n"); | 
|  | } | 
|  |  | 
|  | if (result) | 
|  | result->SetStatus (eReturnStatusSuccessFinishResult); | 
|  | } | 
|  | else | 
|  | { | 
|  | const char *error_cstr = result_valobj_sp->GetError().AsCString(); | 
|  | if (error_cstr && error_cstr[0]) | 
|  | { | 
|  | const size_t error_cstr_len = strlen (error_cstr); | 
|  | const bool ends_with_newline = error_cstr[error_cstr_len - 1] == '\n'; | 
|  | if (strstr(error_cstr, "error:") != error_cstr) | 
|  | error_stream->PutCString ("error: "); | 
|  | error_stream->Write(error_cstr, error_cstr_len); | 
|  | if (!ends_with_newline) | 
|  | error_stream->EOL(); | 
|  | } | 
|  | else | 
|  | { | 
|  | error_stream->PutCString ("error: unknown error\n"); | 
|  | } | 
|  |  | 
|  | if (result) | 
|  | result->SetStatus (eReturnStatusFailed); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | error_stream->Printf ("error: invalid execution context for expression\n"); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void | 
|  | CommandObjectExpression::IOHandlerInputComplete (IOHandler &io_handler, std::string &line) | 
|  | { | 
|  | io_handler.SetIsDone(true); | 
|  | //    StreamSP output_stream = io_handler.GetDebugger().GetAsyncOutputStream(); | 
|  | //    StreamSP error_stream = io_handler.GetDebugger().GetAsyncErrorStream(); | 
|  | StreamFileSP output_sp(io_handler.GetOutputStreamFile()); | 
|  | StreamFileSP error_sp(io_handler.GetErrorStreamFile()); | 
|  |  | 
|  | EvaluateExpression (line.c_str(), | 
|  | output_sp.get(), | 
|  | error_sp.get()); | 
|  | if (output_sp) | 
|  | output_sp->Flush(); | 
|  | if (error_sp) | 
|  | error_sp->Flush(); | 
|  | } | 
|  |  | 
|  | LineStatus | 
|  | CommandObjectExpression::IOHandlerLinesUpdated (IOHandler &io_handler, | 
|  | StringList &lines, | 
|  | uint32_t line_idx, | 
|  | Error &error) | 
|  | { | 
|  | if (line_idx == UINT32_MAX) | 
|  | { | 
|  | // Remove the last line from "lines" so it doesn't appear | 
|  | // in our final expression | 
|  | lines.PopBack(); | 
|  | error.Clear(); | 
|  | return LineStatus::Done; | 
|  | } | 
|  | else if (line_idx + 1 == lines.GetSize()) | 
|  | { | 
|  | // The last line was edited, if this line is empty, then we are done | 
|  | // getting our multiple lines. | 
|  | if (lines[line_idx].empty()) | 
|  | return LineStatus::Done; | 
|  | } | 
|  | return LineStatus::Success; | 
|  | } | 
|  |  | 
|  | void | 
|  | CommandObjectExpression::GetMultilineExpression () | 
|  | { | 
|  | m_expr_lines.clear(); | 
|  | m_expr_line_count = 0; | 
|  |  | 
|  | Debugger &debugger = GetCommandInterpreter().GetDebugger(); | 
|  | const bool multiple_lines = true; // Get multiple lines | 
|  | IOHandlerSP io_handler_sp (new IOHandlerEditline (debugger, | 
|  | "lldb-expr",      // Name of input reader for history | 
|  | NULL,             // No prompt | 
|  | multiple_lines, | 
|  | 1,                // Show line numbers starting at 1 | 
|  | *this)); | 
|  |  | 
|  | StreamFileSP output_sp(io_handler_sp->GetOutputStreamFile()); | 
|  | if (output_sp) | 
|  | { | 
|  | output_sp->PutCString("Enter expressions, then terminate with an empty line to evaluate:\n"); | 
|  | output_sp->Flush(); | 
|  | } | 
|  | debugger.PushIOHandler(io_handler_sp); | 
|  | } | 
|  |  | 
|  | bool | 
|  | CommandObjectExpression::DoExecute | 
|  | ( | 
|  | const char *command, | 
|  | CommandReturnObject &result | 
|  | ) | 
|  | { | 
|  | m_option_group.NotifyOptionParsingStarting(); | 
|  |  | 
|  | const char * expr = NULL; | 
|  |  | 
|  | if (command[0] == '\0') | 
|  | { | 
|  | GetMultilineExpression (); | 
|  | return result.Succeeded(); | 
|  | } | 
|  |  | 
|  | if (command[0] == '-') | 
|  | { | 
|  | // We have some options and these options MUST end with --. | 
|  | const char *end_options = NULL; | 
|  | const char *s = command; | 
|  | while (s && s[0]) | 
|  | { | 
|  | end_options = ::strstr (s, "--"); | 
|  | if (end_options) | 
|  | { | 
|  | end_options += 2; // Get past the "--" | 
|  | if (::isspace (end_options[0])) | 
|  | { | 
|  | expr = end_options; | 
|  | while (::isspace (*expr)) | 
|  | ++expr; | 
|  | break; | 
|  | } | 
|  | } | 
|  | s = end_options; | 
|  | } | 
|  |  | 
|  | if (end_options) | 
|  | { | 
|  | Args args (command, end_options - command); | 
|  | if (!ParseOptions (args, result)) | 
|  | return false; | 
|  |  | 
|  | Error error (m_option_group.NotifyOptionParsingFinished()); | 
|  | if (error.Fail()) | 
|  | { | 
|  | result.AppendError (error.AsCString()); | 
|  | result.SetStatus (eReturnStatusFailed); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // No expression following options | 
|  | if (expr == NULL || expr[0] == '\0') | 
|  | { | 
|  | GetMultilineExpression (); | 
|  | return result.Succeeded(); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (expr == NULL) | 
|  | expr = command; | 
|  |  | 
|  | if (EvaluateExpression (expr, &(result.GetOutputStream()), &(result.GetErrorStream()), &result)) | 
|  | return true; | 
|  |  | 
|  | result.SetStatus (eReturnStatusFailed); | 
|  | return false; | 
|  | } | 
|  |  |