| //===-- 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/InputReader.h" |
| #include "lldb/Core/ValueObjectVariable.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 () |
| { |
| } |
| |
| OptionDefinition |
| CommandObjectExpression::CommandOptions::g_option_table[] = |
| { |
| { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "all-threads", 'a', required_argument, 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', required_argument, NULL, 0, eArgTypeBoolean, "Ignore breakpoint hits while running expressions"}, |
| { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "timeout", 't', required_argument, NULL, 0, eArgTypeUnsignedInteger, "Timeout value (in microseconds) for running the expression."}, |
| { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "unwind-on-error", 'u', required_argument, 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)."}, |
| }; |
| |
| |
| 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; |
| } |
| 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 = false; |
| unwind_on_error = true; |
| } |
| |
| show_summary = true; |
| try_all_threads = true; |
| timeout = 0; |
| } |
| |
| 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), |
| 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; |
| } |
| |
| size_t |
| CommandObjectExpression::MultiLineExpressionCallback |
| ( |
| void *baton, |
| InputReader &reader, |
| lldb::InputReaderAction notification, |
| const char *bytes, |
| size_t bytes_len |
| ) |
| { |
| CommandObjectExpression *cmd_object_expr = (CommandObjectExpression *) baton; |
| bool batch_mode = reader.GetDebugger().GetCommandInterpreter().GetBatchCommandMode(); |
| |
| switch (notification) |
| { |
| case eInputReaderActivate: |
| if (!batch_mode) |
| { |
| StreamSP async_strm_sp(reader.GetDebugger().GetAsyncOutputStream()); |
| if (async_strm_sp) |
| { |
| async_strm_sp->PutCString("Enter expressions, then terminate with an empty line to evaluate:\n"); |
| async_strm_sp->Flush(); |
| } |
| } |
| // Fall through |
| case eInputReaderReactivate: |
| break; |
| |
| case eInputReaderDeactivate: |
| break; |
| |
| case eInputReaderAsynchronousOutputWritten: |
| break; |
| |
| case eInputReaderGotToken: |
| ++cmd_object_expr->m_expr_line_count; |
| if (bytes && bytes_len) |
| { |
| cmd_object_expr->m_expr_lines.append (bytes, bytes_len + 1); |
| } |
| |
| if (bytes_len == 0) |
| reader.SetIsDone(true); |
| break; |
| |
| case eInputReaderInterrupt: |
| cmd_object_expr->m_expr_lines.clear(); |
| reader.SetIsDone (true); |
| if (!batch_mode) |
| { |
| StreamSP async_strm_sp (reader.GetDebugger().GetAsyncOutputStream()); |
| if (async_strm_sp) |
| { |
| async_strm_sp->PutCString("Expression evaluation cancelled.\n"); |
| async_strm_sp->Flush(); |
| } |
| } |
| break; |
| |
| case eInputReaderEndOfFile: |
| reader.SetIsDone (true); |
| break; |
| |
| case eInputReaderDone: |
| if (cmd_object_expr->m_expr_lines.size() > 0) |
| { |
| StreamSP output_stream = reader.GetDebugger().GetAsyncOutputStream(); |
| StreamSP error_stream = reader.GetDebugger().GetAsyncErrorStream(); |
| cmd_object_expr->EvaluateExpression (cmd_object_expr->m_expr_lines.c_str(), |
| output_stream.get(), |
| error_stream.get()); |
| output_stream->Flush(); |
| error_stream->Flush(); |
| } |
| break; |
| } |
| |
| return bytes_len; |
| } |
| |
| 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; |
| |
| ExecutionResults exe_results; |
| |
| bool keep_in_memory = true; |
| |
| EvaluateExpressionOptions options; |
| options.SetCoerceToId(m_varobj_options.use_objc) |
| .SetUnwindOnError(m_command_options.unwind_on_error) |
| .SetIgnoreBreakpoints (m_command_options.ignore_breakpoints) |
| .SetKeepInMemory(keep_in_memory) |
| .SetUseDynamic(m_varobj_options.use_dynamic) |
| .SetRunOthers(m_command_options.try_all_threads) |
| .SetTimeoutUsec(m_command_options.timeout); |
| |
| exe_results = 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); |
| |
| ValueObject::DumpValueObjectOptions options(m_varobj_options.GetAsDumpOptions(true,format)); |
| |
| ValueObject::DumpValueObject (*(output_stream), |
| result_valobj_sp.get(), // Variable object to dump |
| 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; |
| } |
| |
| bool |
| CommandObjectExpression::DoExecute |
| ( |
| const char *command, |
| CommandReturnObject &result |
| ) |
| { |
| m_option_group.NotifyOptionParsingStarting(); |
| |
| const char * expr = NULL; |
| |
| if (command[0] == '\0') |
| { |
| m_expr_lines.clear(); |
| m_expr_line_count = 0; |
| |
| InputReaderSP reader_sp (new InputReader(m_interpreter.GetDebugger())); |
| if (reader_sp) |
| { |
| Error err (reader_sp->Initialize (CommandObjectExpression::MultiLineExpressionCallback, |
| this, // baton |
| eInputReaderGranularityLine, // token size, to pass to callback function |
| NULL, // end token |
| NULL, // prompt |
| true)); // echo input |
| if (err.Success()) |
| { |
| m_interpreter.GetDebugger().PushInputReader (reader_sp); |
| result.SetStatus (eReturnStatusSuccessFinishNoResult); |
| } |
| else |
| { |
| result.AppendError (err.AsCString()); |
| result.SetStatus (eReturnStatusFailed); |
| } |
| } |
| else |
| { |
| result.AppendError("out of memory"); |
| result.SetStatus (eReturnStatusFailed); |
| } |
| 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; |
| } |
| } |
| } |
| |
| if (expr == NULL) |
| expr = command; |
| |
| if (EvaluateExpression (expr, &(result.GetOutputStream()), &(result.GetErrorStream()), &result)) |
| return true; |
| |
| result.SetStatus (eReturnStatusFailed); |
| return false; |
| } |
| |