|  | //===-- 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; | 
|  | const char *explanation = execution_result == lldb::eExpressionInterrupted | 
|  | ? "was interrupted" | 
|  | : "hit a breakpoint"; | 
|  |  | 
|  | 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, | 
|  | "Expression execution %s: %s.", explanation, | 
|  | error_desc); | 
|  | else | 
|  | diagnostic_manager.Printf(lldb::eSeverityError, | 
|  | "Expression execution %s.", explanation); | 
|  |  | 
|  | 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; | 
|  | } | 
|  |  | 
|  | if (execution_result == lldb::eExpressionStoppedForDebug) { | 
|  | diagnostic_manager.PutString( | 
|  | lldb::eSeverityInfo, | 
|  | "Expression 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; | 
|  | } | 
|  |  | 
|  | if (execution_result == lldb::eExpressionThreadVanished) { | 
|  | diagnostic_manager.Printf(lldb::eSeverityError, | 
|  | "Couldn't execute expression: the thread on " | 
|  | "which the expression was being run (0x%" PRIx64 | 
|  | ") exited during its execution.", | 
|  | expr_thread_id); | 
|  | return execution_result; | 
|  | } | 
|  |  | 
|  | if (execution_result != lldb::eExpressionCompleted) { | 
|  | diagnostic_manager.Printf(lldb::eSeverityError, | 
|  | "Couldn't execute expression: 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; | 
|  | } | 
|  |  | 
|  | 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) { | 
|  | IRMemoryMap::AllocationPolicy policy = | 
|  | m_can_interpret ? IRMemoryMap::eAllocationPolicyHostOnly | 
|  | : IRMemoryMap::eAllocationPolicyMirror; | 
|  |  | 
|  | const bool zero_memory = false; | 
|  | if (auto address_or_error = m_execution_unit_sp->Malloc( | 
|  | m_materializer_up->GetStructByteSize(), | 
|  | m_materializer_up->GetStructAlignment(), | 
|  | lldb::ePermissionsReadable | lldb::ePermissionsWritable, policy, | 
|  | zero_memory)) { | 
|  | m_materialized_address = *address_or_error; | 
|  | } else { | 
|  | diagnostic_manager.Printf( | 
|  | lldb::eSeverityError, | 
|  | "Couldn't allocate space for materialized struct: %s", | 
|  | toString(address_or_error.takeError()).c_str()); | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | struct_address = m_materialized_address; | 
|  |  | 
|  | if (m_can_interpret && m_stack_frame_bottom == LLDB_INVALID_ADDRESS) { | 
|  | 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; | 
|  | if (auto address_or_error = m_execution_unit_sp->Malloc( | 
|  | stack_frame_size, 8, | 
|  | lldb::ePermissionsReadable | lldb::ePermissionsWritable, | 
|  | IRMemoryMap::eAllocationPolicyHostOnly, zero_memory)) { | 
|  | m_stack_frame_bottom = *address_or_error; | 
|  | m_stack_frame_top = m_stack_frame_bottom + stack_frame_size; | 
|  | } else { | 
|  | diagnostic_manager.Printf( | 
|  | lldb::eSeverityError, | 
|  | "Couldn't allocate space for the stack frame: %s", | 
|  | toString(address_or_error.takeError()).c_str()); | 
|  | 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; | 
|  | } |