llvm-project/lldb/source/Expression/LLVMUserExpression.cpp
Pavel Labath c34698a811 [lldb] Rename Logging.h to LLDBLog.h and clean up includes
Most of our code was including Log.h even though that is not where the
"lldb" log channel is defined (Log.h defines the generic logging
infrastructure). This worked because Log.h included Logging.h, even
though it should.

After the recent refactor, it became impossible the two files include
each other in this direction (the opposite inclusion is needed), so this
patch removes the workaround that was put in place and cleans up all
files to include the right thing. It also renames the file to LLDBLog to
better reflect its purpose.
2022-02-03 14:47:01 +01:00

371 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/Core/StreamFile.h"
#include "lldb/Core/ValueObjectConstResult.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/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/LLDBLog.h"
#include "lldb/Utility/Log.h"
#include "lldb/Utility/StreamString.h"
using namespace lldb_private;
char LLVMUserExpression::ID;
LLVMUserExpression::LLVMUserExpression(ExecutionContextScope &exe_scope,
llvm::StringRef expr,
llvm::StringRef prefix,
lldb::LanguageType 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_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(
eDiagnosticSeverityError,
"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(
eDiagnosticSeverityError,
"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(
eDiagnosticSeverityError,
"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(eDiagnosticSeverityError,
"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);
if (!interpreter_error.Success()) {
diagnostic_manager.Printf(eDiagnosticSeverityError,
"supposed to interpret, but failed: %s",
interpreter_error.AsCString());
return lldb::eExpressionDiscarded;
}
} else {
if (!exe_ctx.HasThreadScope()) {
diagnostic_manager.Printf(eDiagnosticSeverityError,
"%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(eDiagnosticSeverityError,
"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(eDiagnosticSeverityError, 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(eDiagnosticSeverityError,
"Execution was interrupted, reason: %s.",
error_desc);
else
diagnostic_manager.PutString(eDiagnosticSeverityError,
"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(
eDiagnosticSeverityRemark,
"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(
eDiagnosticSeverityError,
"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(
eDiagnosticSeverityError, "Couldn't execute function; result was %s",
Process::ExecutionResultAsCString(execution_result));
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(eDiagnosticSeverityError,
"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(eDiagnosticSeverityError,
"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(
eDiagnosticSeverityError,
"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(
eDiagnosticSeverityError,
"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;
const size_t 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(
eDiagnosticSeverityError,
"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(eDiagnosticSeverityError,
"Couldn't materialize: %s",
materialize_error.AsCString());
return false;
}
}
return true;
}