llvm-project/lldb/source/Commands/CommandObjectExpression.cpp

512 lines
16 KiB
C++

//===-- 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 "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, "dynamic-value", 'd', required_argument, NULL, 0, eArgTypeBoolean, "Upcast the value resulting from the expression to its dynamic type if available."},
{ 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, breakpoint hit or signal."},
{ LLDB_OPT_SET_2 , false, "object-description", 'o', no_argument, NULL, 0, eArgTypeNone, "Print the object description of the value resulting from the expression."},
};
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 char short_option = (char) 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 'o':
print_object = true;
break;
case 'd':
{
bool success;
bool result;
result = Args::StringToBoolean(option_arg, true, &success);
if (!success)
error.SetErrorStringWithFormat("invalid dynamic value setting: \"%s\"", option_arg);
else
{
if (result)
use_dynamic = eLazyBoolYes;
else
use_dynamic = eLazyBoolNo;
}
}
break;
case 'u':
{
bool success;
unwind_on_error = Args::StringToBoolean(option_arg, true, &success);
if (!success)
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)
{
use_dynamic = eLazyBoolCalculate;
print_object = false;
unwind_on_error = true;
show_types = true;
show_summary = true;
}
const OptionDefinition*
CommandObjectExpression::CommandOptions::GetDefinitions ()
{
return g_option_table;
}
CommandObjectExpression::CommandObjectExpression (CommandInterpreter &interpreter) :
CommandObject (interpreter,
"expression",
"Evaluate a C/ObjC/C++ expression in the current program context, using variables currently in scope.",
NULL),
m_option_group (interpreter),
m_format_options (eFormatDefault),
m_command_options (),
m_expr_line_count (0),
m_expr_lines ()
{
SetHelpLong(
"Examples: \n\
\n\
expr my_struct->a = my_array[3] \n\
expr -f bin -- (index * 8) + 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.Finalize();
}
CommandObjectExpression::~CommandObjectExpression ()
{
}
Options *
CommandObjectExpression::GetOptions ()
{
return &m_option_group;
}
bool
CommandObjectExpression::Execute
(
Args& command,
CommandReturnObject &result
)
{
return false;
}
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
)
{
Target *target = m_interpreter.GetExecutionContext().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;
lldb::DynamicValueType use_dynamic;
// If use dynamic is not set, get it from the target:
switch (m_command_options.use_dynamic)
{
case eLazyBoolCalculate:
use_dynamic = target->GetPreferDynamicValue();
break;
case eLazyBoolYes:
use_dynamic = lldb::eDynamicCanRunTarget;
break;
case eLazyBoolNo:
use_dynamic = lldb::eNoDynamicValues;
break;
}
exe_results = target->EvaluateExpression (expr,
m_interpreter.GetExecutionContext().GetFramePtr(),
eExecutionPolicyOnlyWhenNeeded,
m_command_options.print_object,
m_command_options.unwind_on_error,
keep_in_memory,
use_dynamic,
result_valobj_sp);
if (exe_results == eExecutionInterrupted && !m_command_options.unwind_on_error)
{
uint32_t start_frame = 0;
uint32_t num_frames = 1;
uint32_t num_frames_with_source = 0;
Thread *thread = m_interpreter.GetExecutionContext().GetThreadPtr();
if (thread)
{
thread->GetStatus (result->GetOutputStream(),
start_frame,
num_frames,
num_frames_with_source);
}
else
{
Process *process = m_interpreter.GetExecutionContext().GetProcessPtr();
if (process)
{
bool only_threads_with_stop_reason = true;
process->GetThreadStatus (result->GetOutputStream(),
only_threads_with_stop_reason,
start_frame,
num_frames,
num_frames_with_source);
}
}
}
if (result_valobj_sp)
{
if (result_valobj_sp->GetError().Success())
{
Format format = m_format_options.GetFormat();
if (format != eFormatDefault)
result_valobj_sp->SetFormat (format);
ValueObject::DumpValueObjectOptions options;
options.SetMaximumPointerDepth(0)
.SetMaximumDepth(UINT32_MAX)
.SetShowLocation(false)
.SetShowTypes(m_command_options.show_types)
.SetUseObjectiveC(m_command_options.print_object)
.SetUseDynamicType(use_dynamic)
.SetScopeChecked(true)
.SetFlatOutput(false)
.SetUseSyntheticValue(true)
.SetIgnoreCap(false)
.SetFormat(format)
.SetSummary()
.SetShowSummary(!m_command_options.print_object);
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)
{
error_stream->PutCString("<no result>\n");
if (result)
result->SetStatus (eReturnStatusSuccessFinishResult);
}
else
{
const char *error_cstr = result_valobj_sp->GetError().AsCString();
if (error_cstr && error_cstr[0])
{
int 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::ExecuteRawCommandString
(
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;
}