Files
clang-p2996/lldb/source/Expression/LLVMUserExpression.cpp
Jonas Devlieghere b852fb1ec5 [lldb] Move ValueObject into its own library (NFC) (#113393)
ValueObject is part of lldbCore for historical reasons, but conceptually
it deserves to be its own library. This does introduce a (link-time) circular
dependency between lldbCore and lldbValueObject, which is unfortunate
but probably unavoidable because so many things in LLDB rely on
ValueObject. We already have cycles and these libraries are never built
as dylibs so while this doesn't improve the situation, it also doesn't
make things worse.

The header includes were updated with the following command:

```
find . -type f -exec sed -i.bak "s%include \"lldb/Core/ValueObject%include \"lldb/ValueObject/ValueObject%" '{}' \;
```
2024-10-24 20:20:48 -07:00

378 lines
14 KiB
C++

//===-- LLVMUserExpression.cpp --------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "lldb/Expression/LLVMUserExpression.h"
#include "lldb/Core/Module.h"
#include "lldb/Expression/DiagnosticManager.h"
#include "lldb/Expression/ExpressionVariable.h"
#include "lldb/Expression/IRExecutionUnit.h"
#include "lldb/Expression/IRInterpreter.h"
#include "lldb/Expression/Materializer.h"
#include "lldb/Host/HostInfo.h"
#include "lldb/Symbol/Block.h"
#include "lldb/Symbol/Function.h"
#include "lldb/Symbol/ObjectFile.h"
#include "lldb/Symbol/SymbolVendor.h"
#include "lldb/Symbol/Type.h"
#include "lldb/Symbol/VariableList.h"
#include "lldb/Target/ABI.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/StackFrame.h"
#include "lldb/Target/Target.h"
#include "lldb/Target/ThreadPlan.h"
#include "lldb/Target/ThreadPlanCallUserExpression.h"
#include "lldb/Utility/ConstString.h"
#include "lldb/Utility/ErrorMessages.h"
#include "lldb/Utility/LLDBLog.h"
#include "lldb/Utility/Log.h"
#include "lldb/Utility/StreamString.h"
#include "lldb/ValueObject/ValueObjectConstResult.h"
using namespace lldb;
using namespace lldb_private;
char LLVMUserExpression::ID;
LLVMUserExpression::LLVMUserExpression(ExecutionContextScope &exe_scope,
llvm::StringRef expr,
llvm::StringRef prefix,
SourceLanguage language,
ResultType desired_type,
const EvaluateExpressionOptions &options)
: UserExpression(exe_scope, expr, prefix, language, desired_type, options),
m_stack_frame_bottom(LLDB_INVALID_ADDRESS),
m_stack_frame_top(LLDB_INVALID_ADDRESS), m_allow_cxx(false),
m_allow_objc(false), m_transformed_text(), m_execution_unit_sp(),
m_materializer_up(), m_jit_module_wp(), m_target(nullptr),
m_can_interpret(false), m_materialized_address(LLDB_INVALID_ADDRESS) {}
LLVMUserExpression::~LLVMUserExpression() {
if (m_target) {
lldb::ModuleSP jit_module_sp(m_jit_module_wp.lock());
if (jit_module_sp)
m_target->GetImages().Remove(jit_module_sp);
}
}
lldb::ExpressionResults
LLVMUserExpression::DoExecute(DiagnosticManager &diagnostic_manager,
ExecutionContext &exe_ctx,
const EvaluateExpressionOptions &options,
lldb::UserExpressionSP &shared_ptr_to_me,
lldb::ExpressionVariableSP &result) {
// The expression log is quite verbose, and if you're just tracking the
// execution of the expression, it's quite convenient to have these logs come
// out with the STEP log as well.
Log *log(GetLog(LLDBLog::Expressions | LLDBLog::Step));
if (m_jit_start_addr == LLDB_INVALID_ADDRESS && !m_can_interpret) {
diagnostic_manager.PutString(
lldb::eSeverityError,
"Expression can't be run, because there is no JIT compiled function");
return lldb::eExpressionSetupError;
}
lldb::addr_t struct_address = LLDB_INVALID_ADDRESS;
if (!PrepareToExecuteJITExpression(diagnostic_manager, exe_ctx,
struct_address)) {
diagnostic_manager.Printf(
lldb::eSeverityError,
"errored out in %s, couldn't PrepareToExecuteJITExpression",
__FUNCTION__);
return lldb::eExpressionSetupError;
}
lldb::addr_t function_stack_bottom = LLDB_INVALID_ADDRESS;
lldb::addr_t function_stack_top = LLDB_INVALID_ADDRESS;
if (m_can_interpret) {
llvm::Module *module = m_execution_unit_sp->GetModule();
llvm::Function *function = m_execution_unit_sp->GetFunction();
if (!module || !function) {
diagnostic_manager.PutString(
lldb::eSeverityError, "supposed to interpret, but nothing is there");
return lldb::eExpressionSetupError;
}
Status interpreter_error;
std::vector<lldb::addr_t> args;
if (!AddArguments(exe_ctx, args, struct_address, diagnostic_manager)) {
diagnostic_manager.Printf(lldb::eSeverityError,
"errored out in %s, couldn't AddArguments",
__FUNCTION__);
return lldb::eExpressionSetupError;
}
function_stack_bottom = m_stack_frame_bottom;
function_stack_top = m_stack_frame_top;
IRInterpreter::Interpret(*module, *function, args, *m_execution_unit_sp,
interpreter_error, function_stack_bottom,
function_stack_top, exe_ctx, options.GetTimeout());
if (!interpreter_error.Success()) {
diagnostic_manager.Printf(lldb::eSeverityError,
"supposed to interpret, but failed: %s",
interpreter_error.AsCString());
return lldb::eExpressionDiscarded;
}
} else {
if (!exe_ctx.HasThreadScope()) {
diagnostic_manager.Printf(lldb::eSeverityError,
"%s called with no thread selected",
__FUNCTION__);
return lldb::eExpressionSetupError;
}
// Store away the thread ID for error reporting, in case it exits
// during execution:
lldb::tid_t expr_thread_id = exe_ctx.GetThreadRef().GetID();
Address wrapper_address(m_jit_start_addr);
std::vector<lldb::addr_t> args;
if (!AddArguments(exe_ctx, args, struct_address, diagnostic_manager)) {
diagnostic_manager.Printf(lldb::eSeverityError,
"errored out in %s, couldn't AddArguments",
__FUNCTION__);
return lldb::eExpressionSetupError;
}
lldb::ThreadPlanSP call_plan_sp(new ThreadPlanCallUserExpression(
exe_ctx.GetThreadRef(), wrapper_address, args, options,
shared_ptr_to_me));
StreamString ss;
if (!call_plan_sp || !call_plan_sp->ValidatePlan(&ss)) {
diagnostic_manager.PutString(lldb::eSeverityError, ss.GetString());
return lldb::eExpressionSetupError;
}
ThreadPlanCallUserExpression *user_expression_plan =
static_cast<ThreadPlanCallUserExpression *>(call_plan_sp.get());
lldb::addr_t function_stack_pointer =
user_expression_plan->GetFunctionStackPointer();
function_stack_bottom = function_stack_pointer - HostInfo::GetPageSize();
function_stack_top = function_stack_pointer;
LLDB_LOGF(log,
"-- [UserExpression::Execute] Execution of expression begins --");
if (exe_ctx.GetProcessPtr())
exe_ctx.GetProcessPtr()->SetRunningUserExpression(true);
lldb::ExpressionResults execution_result =
exe_ctx.GetProcessRef().RunThreadPlan(exe_ctx, call_plan_sp, options,
diagnostic_manager);
if (exe_ctx.GetProcessPtr())
exe_ctx.GetProcessPtr()->SetRunningUserExpression(false);
LLDB_LOGF(log, "-- [UserExpression::Execute] Execution of expression "
"completed --");
if (execution_result == lldb::eExpressionInterrupted ||
execution_result == lldb::eExpressionHitBreakpoint) {
const char *error_desc = nullptr;
if (user_expression_plan) {
if (auto real_stop_info_sp = user_expression_plan->GetRealStopInfo())
error_desc = real_stop_info_sp->GetDescription();
}
if (error_desc)
diagnostic_manager.Printf(lldb::eSeverityError,
"Execution was interrupted, reason: %s.",
error_desc);
else
diagnostic_manager.PutString(lldb::eSeverityError,
"Execution was interrupted.");
if ((execution_result == lldb::eExpressionInterrupted &&
options.DoesUnwindOnError()) ||
(execution_result == lldb::eExpressionHitBreakpoint &&
options.DoesIgnoreBreakpoints()))
diagnostic_manager.AppendMessageToDiagnostic(
"The process has been returned to the state before expression "
"evaluation.");
else {
if (execution_result == lldb::eExpressionHitBreakpoint)
user_expression_plan->TransferExpressionOwnership();
diagnostic_manager.AppendMessageToDiagnostic(
"The process has been left at the point where it was "
"interrupted, "
"use \"thread return -x\" to return to the state before "
"expression evaluation.");
}
return execution_result;
} else if (execution_result == lldb::eExpressionStoppedForDebug) {
diagnostic_manager.PutString(
lldb::eSeverityInfo,
"Execution was halted at the first instruction of the expression "
"function because \"debug\" was requested.\n"
"Use \"thread return -x\" to return to the state before expression "
"evaluation.");
return execution_result;
} else if (execution_result == lldb::eExpressionThreadVanished) {
diagnostic_manager.Printf(
lldb::eSeverityError,
"Couldn't complete execution; the thread "
"on which the expression was being run: 0x%" PRIx64
" exited during its execution.",
expr_thread_id);
return execution_result;
} else if (execution_result != lldb::eExpressionCompleted) {
diagnostic_manager.Printf(lldb::eSeverityError,
"Couldn't execute function; result was %s",
toString(execution_result).c_str());
return execution_result;
}
}
if (FinalizeJITExecution(diagnostic_manager, exe_ctx, result,
function_stack_bottom, function_stack_top)) {
return lldb::eExpressionCompleted;
} else {
return lldb::eExpressionResultUnavailable;
}
}
bool LLVMUserExpression::FinalizeJITExecution(
DiagnosticManager &diagnostic_manager, ExecutionContext &exe_ctx,
lldb::ExpressionVariableSP &result, lldb::addr_t function_stack_bottom,
lldb::addr_t function_stack_top) {
Log *log = GetLog(LLDBLog::Expressions);
LLDB_LOGF(log, "-- [UserExpression::FinalizeJITExecution] Dematerializing "
"after execution --");
if (!m_dematerializer_sp) {
diagnostic_manager.Printf(lldb::eSeverityError,
"Couldn't apply expression side effects : no "
"dematerializer is present");
return false;
}
Status dematerialize_error;
m_dematerializer_sp->Dematerialize(dematerialize_error, function_stack_bottom,
function_stack_top);
if (!dematerialize_error.Success()) {
diagnostic_manager.Printf(lldb::eSeverityError,
"Couldn't apply expression side effects : %s",
dematerialize_error.AsCString("unknown error"));
return false;
}
result =
GetResultAfterDematerialization(exe_ctx.GetBestExecutionContextScope());
if (result)
result->TransferAddress();
m_dematerializer_sp.reset();
return true;
}
bool LLVMUserExpression::PrepareToExecuteJITExpression(
DiagnosticManager &diagnostic_manager, ExecutionContext &exe_ctx,
lldb::addr_t &struct_address) {
lldb::TargetSP target;
lldb::ProcessSP process;
lldb::StackFrameSP frame;
if (!LockAndCheckContext(exe_ctx, target, process, frame)) {
diagnostic_manager.PutString(
lldb::eSeverityError,
"The context has changed before we could JIT the expression!");
return false;
}
if (m_jit_start_addr != LLDB_INVALID_ADDRESS || m_can_interpret) {
if (m_materialized_address == LLDB_INVALID_ADDRESS) {
Status alloc_error;
IRMemoryMap::AllocationPolicy policy =
m_can_interpret ? IRMemoryMap::eAllocationPolicyHostOnly
: IRMemoryMap::eAllocationPolicyMirror;
const bool zero_memory = false;
m_materialized_address = m_execution_unit_sp->Malloc(
m_materializer_up->GetStructByteSize(),
m_materializer_up->GetStructAlignment(),
lldb::ePermissionsReadable | lldb::ePermissionsWritable, policy,
zero_memory, alloc_error);
if (!alloc_error.Success()) {
diagnostic_manager.Printf(
lldb::eSeverityError,
"Couldn't allocate space for materialized struct: %s",
alloc_error.AsCString());
return false;
}
}
struct_address = m_materialized_address;
if (m_can_interpret && m_stack_frame_bottom == LLDB_INVALID_ADDRESS) {
Status alloc_error;
size_t stack_frame_size = target->GetExprAllocSize();
if (stack_frame_size == 0) {
ABISP abi_sp;
if (process && (abi_sp = process->GetABI()))
stack_frame_size = abi_sp->GetStackFrameSize();
else
stack_frame_size = 512 * 1024;
}
const bool zero_memory = false;
m_stack_frame_bottom = m_execution_unit_sp->Malloc(
stack_frame_size, 8,
lldb::ePermissionsReadable | lldb::ePermissionsWritable,
IRMemoryMap::eAllocationPolicyHostOnly, zero_memory, alloc_error);
m_stack_frame_top = m_stack_frame_bottom + stack_frame_size;
if (!alloc_error.Success()) {
diagnostic_manager.Printf(
lldb::eSeverityError,
"Couldn't allocate space for the stack frame: %s",
alloc_error.AsCString());
return false;
}
}
Status materialize_error;
m_dematerializer_sp = m_materializer_up->Materialize(
frame, *m_execution_unit_sp, struct_address, materialize_error);
if (!materialize_error.Success()) {
diagnostic_manager.Printf(lldb::eSeverityError,
"Couldn't materialize: %s",
materialize_error.AsCString());
return false;
}
}
return true;
}