| //===-- 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/STLExtras.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, 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, NULL, 0, eArgTypeBoolean, "Ignore breakpoint hits while running expressions"}, |
| { 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."}, |
| { 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)."}, |
| { 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)."}, |
| { 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."}, |
| }; |
| |
| |
| uint32_t |
| CommandObjectExpression::CommandOptions::GetNumDefinitions () |
| { |
| return llvm::array_lengthof(g_option_table); |
| } |
| |
| 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 running 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\ |
| \n\ |
| Continuing evaluation after a breakpoint:\n\ |
| If the \"-i false\" option is used, and execution is interrupted by a breakpoint hit, once\n\ |
| you are done with your investigation, you can either remove the expression execution frames\n\ |
| from the stack with \"thread return -x\" or if you are still interested in the expression result\n\ |
| you can issue the \"continue\" command and the expression evaluation will complete and the\n\ |
| expression result will be available using the \"thread.completed-expression\" key in the thread\n\ |
| format.\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; |
| } |
| |