For Hexagon we want to be able to call functions during debugging, however currently lldb only supports this when there is JIT support.
Although emulation using IR interpretation is an alternative, it is currently limited in that it can't make function calls.
In this patch we have extended the IR interpreter so that it can execute a function call on the target using register manipulation.
To do this we need to handle the Call IR instruction, passing arguments to a new thread plan and collecting any return values to pass back into the IR interpreter.
The new thread plan is needed to call an alternative ABI interface of "ABI::PerpareTrivialCall()", allowing more detailed information about arguments and return values.
Reviewers: jingham, spyffe
Subscribers: emaste, lldb-commits, ted, ADodds, deepak2427
Differential Revision: http://reviews.llvm.org/D9404
llvm-svn: 242137
static bool
CanInterpret (llvm::Module &module,
llvm::Function &function,
- lldb_private::Error &error);
+ lldb_private::Error &error,
+ const bool support_function_calls);
static bool
Interpret (llvm::Module &module,
lldb_private::IRMemoryMap &memory_map,
lldb_private::Error &error,
lldb::addr_t stack_frame_bottom,
- lldb::addr_t stack_frame_top);
+ lldb::addr_t stack_frame_top,
+ lldb_private::ExecutionContext &exe_ctx);
private:
static bool
void ReadMemory (uint8_t *bytes, lldb::addr_t process_address, size_t size, Error &error);
void ReadScalarFromMemory (Scalar &scalar, lldb::addr_t process_address, size_t size, Error &error);
void ReadPointerFromMemory (lldb::addr_t *address, lldb::addr_t process_address, Error &error);
-
+ bool GetAllocSize(lldb::addr_t address, size_t &size);
void GetMemoryData (DataExtractor &extractor, lldb::addr_t process_address, size_t size, Error &error);
lldb::ByteOrder GetByteOrder();
};
eType type; /* value of eType */
size_t size; /* size in bytes of this argument */
- union {
- lldb::addr_t value; /* literal value */
- uint8_t *data; /* host data pointer */
- };
+
+ lldb::addr_t value; /* literal value */
+ std::unique_ptr<uint8_t[]> data_ap; /* host data pointer */
};
virtual
lldb::addr_t returnAddress,
llvm::ArrayRef<lldb::addr_t> args) const = 0;
- // Prepare trivial call used from ThreadPlanFunctionCallGDB
+ // Prepare trivial call used from ThreadPlanFunctionCallUsingABI
// AD:
// . Because i don't want to change other ABI's this is not declared pure virtual.
// The dummy implementation will simply fail. Only HexagonABI will currently
/// True if execution of JIT code is possible; false otherwise.
//------------------------------------------------------------------
void SetCanJIT (bool can_jit);
+
+ //------------------------------------------------------------------
+ /// Determines whether executing function calls using the interpreter
+ /// is possible for this process.
+ ///
+ /// @return
+ /// True if possible; false otherwise.
+ //------------------------------------------------------------------
+ bool CanInterpretFunctionCalls ()
+ {
+ return m_can_interpret_function_calls;
+ }
//------------------------------------------------------------------
+ /// Sets whether executing function calls using the interpreter
+ /// is possible for this process.
+ ///
+ /// @param[in] can_interpret_function_calls
+ /// True if possible; false otherwise.
+ //------------------------------------------------------------------
+ void SetCanInterpretFunctionCalls (bool can_interpret_function_calls)
+ {
+ m_can_interpret_function_calls = can_interpret_function_calls;
+ }
+
+ //------------------------------------------------------------------
+ /// Sets whether executing code in this process is possible.
+ /// This could be either through JIT or interpreting.
+ ///
+ /// @param[in] can_run_code
+ /// True if execution of code is possible; false otherwise.
+ //------------------------------------------------------------------
+ void SetCanRunCode (bool can_run_code);
+
+ //------------------------------------------------------------------
/// Actually deallocate memory in the process.
///
/// This function will deallocate memory in the process's address
lldb::StateType m_last_broadcast_state; /// This helps with the Public event coalescing in ShouldBroadcastEvent.
std::map<lldb::addr_t,lldb::addr_t> m_resolved_indirect_addresses;
bool m_destroy_in_process;
+ bool m_can_interpret_function_calls; // Some targets, e.g the OSX kernel, don't support the ability to modify the stack.
enum {
eCanJITDontKnow= 0,
llvm::ArrayRef<lldb::addr_t> args,
const EvaluateExpressionOptions &options);
+ ThreadPlanCallFunction(Thread &thread,
+ const Address &function,
+ const EvaluateExpressionOptions &options);
+
virtual
~ThreadPlanCallFunction ();
virtual bool
DoPlanExplainsStop (Event *event_ptr);
-private:
+ virtual void
+ SetReturnValue();
bool
ConstructorSetup (Thread &thread,
bool
BreakpointsExplainStop ();
-
+
bool m_valid;
bool m_stop_other_threads;
bool m_unwind_on_error;
// it's nice to know the real stop reason.
// This gets set in DoTakedown.
StreamString m_constructor_errors;
- ClangASTType m_return_type;
lldb::ValueObjectSP m_return_valobj_sp; // If this contains a valid pointer, use the ABI to extract values when complete
bool m_takedown_done; // We want to ensure we only do the takedown once. This ensures that.
bool m_should_clear_objc_exception_bp;
bool m_should_clear_cxx_exception_bp;
lldb::addr_t m_stop_address; // This is the address we stopped at. Also set in DoTakedown;
+private:
+ ClangASTType m_return_type;
DISALLOW_COPY_AND_ASSIGN (ThreadPlanCallFunction);
};
--- /dev/null
+//===-- ThreadPlanCallFunctionUsingABI.h --------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef liblldb_ThreadPlanCallFunctionUsingABI_h_
+#define liblldb_ThreadPlanCallFunctionUsingABI_h_
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "lldb/lldb-private.h"
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlanCallFunction.h"
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/IR/Type.h"
+
+namespace lldb_private {
+
+class ThreadPlanCallFunctionUsingABI : public ThreadPlanCallFunction
+{
+ // Create a thread plan to call a function at the address passed in the "function"
+ // argument, this function is executed using register manipulation instead of JIT.
+ // Class derives from ThreadPlanCallFunction and differs by calling a alternative
+ // ABI interface ABI::PrepareTrivialCall() which provides more detailed information.
+public:
+ ThreadPlanCallFunctionUsingABI (Thread &thread,
+ const Address &function_address,
+ llvm::Type &function_prototype,
+ llvm::Type &return_type,
+ llvm::ArrayRef<ABI::CallArgument> args,
+ const EvaluateExpressionOptions &options);
+
+ ~ThreadPlanCallFunctionUsingABI ();
+
+ void
+ GetDescription (Stream *s, lldb::DescriptionLevel level) override;
+
+protected:
+ void
+ SetReturnValue () override;
+
+
+private:
+ llvm::Type &m_return_type;
+ DISALLOW_COPY_AND_ASSIGN (ThreadPlanCallFunctionUsingABI);
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_ThreadPlanCallFunctionUsingABI_h_
E7723D4B1AC4A944002BA082 /* RegisterContextPOSIX_arm64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterContextPOSIX_arm64.h; path = Utility/RegisterContextPOSIX_arm64.h; sourceTree = "<group>"; };
E778E99F1B062D1700247609 /* EmulateInstructionMIPS.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = EmulateInstructionMIPS.cpp; sourceTree = "<group>"; };
E778E9A01B062D1700247609 /* EmulateInstructionMIPS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = EmulateInstructionMIPS.h; sourceTree = "<group>"; };
+ EBDD01241B39B62F00E04792 /* ThreadPlanCallFunctionUsingABI.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ThreadPlanCallFunctionUsingABI.cpp; path = source/Target/ThreadPlanCallFunctionUsingABI.cpp; sourceTree = "<group>"; };
+ EBDD01261B39B66700E04792 /* ThreadPlanCallFunctionUsingABI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = ThreadPlanCallFunctionUsingABI.h; path = include/lldb/Target/ThreadPlanCallFunctionUsingABI.h; sourceTree = "<group>"; };
ED88244F15114CA200BC98B9 /* main.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = "<group>"; };
ED88245215114CFC00BC98B9 /* LauncherRootXPCService.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = LauncherRootXPCService.mm; sourceTree = "<group>"; };
EDB919B214F6EC85008FF64B /* LauncherXPCService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LauncherXPCService.h; sourceTree = "<group>"; };
26BC7DEF10F1B80200F91463 /* Target */ = {
isa = PBXGroup;
children = (
+ EBDD01261B39B66700E04792 /* ThreadPlanCallFunctionUsingABI.h */,
+ EBDD01241B39B62F00E04792 /* ThreadPlanCallFunctionUsingABI.cpp */,
8CF02AE019DCBF3B00B14BE0 /* InstrumentationRuntime.h */,
8CF02ADF19DCBF3B00B14BE0 /* InstrumentationRuntime.cpp */,
8CF02AEE19DD15CF00B14BE0 /* InstrumentationRuntimeStopInfo.h */,
26744EF11338317700EF765A /* GDBRemoteCommunicationClient.cpp in Sources */,
26744EF31338317700EF765A /* GDBRemoteCommunicationServer.cpp in Sources */,
264A97BF133918BC0017F0BE /* PlatformRemoteGDBServer.cpp in Sources */,
+ EBDD01251B39B62F00E04792 /* ThreadPlanCallFunctionUsingABI.cpp in Sources */,
2697A54D133A6305004E4240 /* PlatformDarwin.cpp in Sources */,
26651A18133BF9E0005B64B7 /* Opcode.cpp in Sources */,
3FDFED0B19B7C8DE009756A7 /* HostThreadMacOSX.mm in Sources */,
bool ir_can_run = ir_for_target.runOnModule(*execution_unit_sp->GetModule());
Error interpret_error;
+ Process *process = exe_ctx.GetProcessPtr();
- can_interpret = IRInterpreter::CanInterpret(*execution_unit_sp->GetModule(), *execution_unit_sp->GetFunction(), interpret_error);
+ bool interpret_function_calls = !process ? false : process->CanInterpretFunctionCalls();
+ can_interpret = IRInterpreter::CanInterpret(*execution_unit_sp->GetModule(), *execution_unit_sp->GetFunction(), interpret_error, interpret_function_calls);
- Process *process = exe_ctx.GetProcessPtr();
if (!ir_can_run)
{
*m_execution_unit_sp.get(),
interpreter_error,
function_stack_bottom,
- function_stack_top);
+ function_stack_top,
+ exe_ctx);
if (!interpreter_error.Success())
{
#include "lldb/Core/DataExtractor.h"
#include "lldb/Core/Error.h"
#include "lldb/Core/Log.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/Module.h"
#include "lldb/Core/Scalar.h"
#include "lldb/Core/StreamString.h"
+#include "lldb/Core/ValueObject.h"
#include "lldb/Expression/IRMemoryMap.h"
#include "lldb/Expression/IRInterpreter.h"
#include "lldb/Host/Endian.h"
+#include "lldb/Target/ABI.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlan.h"
+#include "lldb/Target/ThreadPlanCallFunctionUsingABI.h"
+
#include "llvm/IR/Constants.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/raw_ostream.h"
bool
IRInterpreter::CanInterpret (llvm::Module &module,
llvm::Function &function,
- lldb_private::Error &error)
+ lldb_private::Error &error,
+ const bool support_function_calls)
{
lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS));
return false;
}
- if (!CanIgnoreCall(call_inst))
+ if (!CanIgnoreCall(call_inst) && !support_function_calls)
{
if (log)
log->Printf("Unsupported instruction: %s", PrintValue(ii).c_str());
lldb_private::IRMemoryMap &memory_map,
lldb_private::Error &error,
lldb::addr_t stack_frame_bottom,
- lldb::addr_t stack_frame_top)
+ lldb::addr_t stack_frame_top,
+ lldb_private::ExecutionContext &exe_ctx)
{
lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS));
{
default:
break;
- case Instruction::Call:
- {
- const CallInst *call_inst = dyn_cast<CallInst>(inst);
-
- if (!call_inst)
- {
- if (log)
- log->Printf("getOpcode() returns %s, but instruction is not a CallInst", inst->getOpcodeName());
- error.SetErrorToGenericError();
- error.SetErrorString(interpreter_internal_error);
- return false;
- }
- if (!CanIgnoreCall(call_inst))
- {
- if (log)
- log->Printf("The interpreter shouldn't have accepted %s", PrintValue(call_inst).c_str());
- error.SetErrorToGenericError();
- error.SetErrorString(interpreter_internal_error);
- return false;
- }
- }
- break;
case Instruction::Add:
case Instruction::Sub:
case Instruction::Mul:
}
}
break;
+ case Instruction::Call:
+ {
+ const CallInst *call_inst = dyn_cast<CallInst>(inst);
+
+ if (!call_inst)
+ {
+ if (log)
+ log->Printf("getOpcode() returns %s, but instruction is not a CallInst", inst->getOpcodeName());
+ error.SetErrorToGenericError();
+ error.SetErrorString(interpreter_internal_error);
+ return false;
+ }
+
+ if (CanIgnoreCall(call_inst))
+ break;
+
+ // Get the return type
+ llvm::Type *returnType = call_inst->getType();
+ if (returnType == nullptr)
+ {
+ error.SetErrorToGenericError();
+ error.SetErrorString("unable to access return type");
+ return false;
+ }
+
+ // Work with void, integer and pointer return types
+ if (!returnType->isVoidTy() &&
+ !returnType->isIntegerTy() &&
+ !returnType->isPointerTy())
+ {
+ error.SetErrorToGenericError();
+ error.SetErrorString("return type is not supported");
+ return false;
+ }
+
+ // Check we can actually get a thread
+ if (exe_ctx.GetThreadPtr() == nullptr)
+ {
+ error.SetErrorToGenericError();
+ error.SetErrorStringWithFormat("unable to acquire thread");
+ return false;
+ }
+
+ // Make sure we have a valid process
+ if (!exe_ctx.GetProcessPtr())
+ {
+ error.SetErrorToGenericError();
+ error.SetErrorStringWithFormat("unable to get the process");
+ return false;
+ }
+
+ // Find the address of the callee function
+ lldb_private::Scalar I;
+ const llvm::Value *val = call_inst->getCalledValue();
+
+ if (!frame.EvaluateValue(I, val, module))
+ {
+ error.SetErrorToGenericError();
+ error.SetErrorString("unable to get address of function");
+ return false;
+ }
+ lldb_private::Address funcAddr(I.ULongLong(LLDB_INVALID_ADDRESS));
+
+ lldb_private::StreamString error_stream;
+ lldb_private::EvaluateExpressionOptions options;
+
+ // We generally receive a function pointer which we must dereference
+ llvm::Type* prototype = val->getType();
+ if (!prototype->isPointerTy())
+ {
+ error.SetErrorToGenericError();
+ error.SetErrorString("call need function pointer");
+ return false;
+ }
+
+ // Dereference the function pointer
+ prototype = prototype->getPointerElementType();
+ if (!(prototype->isFunctionTy() || prototype->isFunctionVarArg()))
+ {
+ error.SetErrorToGenericError();
+ error.SetErrorString("call need function pointer");
+ return false;
+ }
+
+ // Find number of arguments
+ const int numArgs = call_inst->getNumArgOperands();
+
+ // We work with a fixed array of 16 arguments which is our upper limit
+ static lldb_private::ABI::CallArgument rawArgs[16];
+ if (numArgs >= 16)
+ {
+ error.SetErrorToGenericError();
+ error.SetErrorStringWithFormat("function takes too many arguments");
+ return false;
+ }
+
+ // Push all function arguments to the argument list that will
+ // be passed to the call function thread plan
+ for (int i = 0; i < numArgs; i++)
+ {
+ // Get details of this argument
+ llvm::Value *arg_op = call_inst->getArgOperand(i);
+ llvm::Type *arg_ty = arg_op->getType();
+
+ // Ensure that this argument is an supported type
+ if (!arg_ty->isIntegerTy() && !arg_ty->isPointerTy())
+ {
+ error.SetErrorToGenericError();
+ error.SetErrorStringWithFormat("argument %d must be integer type", i);
+ return false;
+ }
+
+ // Extract the arguments value
+ lldb_private::Scalar tmp_op = 0;
+ if (!frame.EvaluateValue(tmp_op, arg_op, module))
+ {
+ error.SetErrorToGenericError();
+ error.SetErrorStringWithFormat("unable to evaluate argument %d", i);
+ return false;
+ }
+
+ // Check if this is a string literal or constant string pointer
+ if (arg_ty->isPointerTy())
+ {
+ // Pointer to just one type
+ assert(arg_ty->getNumContainedTypes() == 1);
+
+ lldb::addr_t addr = tmp_op.ULongLong();
+ size_t dataSize = 0;
+
+ if (memory_map.GetAllocSize(addr, dataSize))
+ {
+ // Create the required buffer
+ rawArgs[i].size = dataSize;
+ rawArgs[i].data_ap.reset(new uint8_t[dataSize + 1]);
+
+ // Read string from host memory
+ memory_map.ReadMemory(rawArgs[i].data_ap.get(), addr, dataSize, error);
+ if (error.Fail())
+ {
+ assert(!"we have failed to read the string from memory");
+ return false;
+ }
+ // Add null terminator
+ rawArgs[i].data_ap[dataSize] = '\0';
+ rawArgs[i].type = lldb_private::ABI::CallArgument::HostPointer;
+ }
+ else
+ {
+ assert(!"unable to locate host data for transfer to device");
+ return false;
+ }
+ }
+ else /* if ( arg_ty->isPointerTy() ) */
+ {
+ rawArgs[i].type = lldb_private::ABI::CallArgument::TargetValue;
+ // Get argument size in bytes
+ rawArgs[i].size = arg_ty->getIntegerBitWidth() / 8;
+ // Push value into argument list for thread plan
+ rawArgs[i].value = tmp_op.ULongLong();
+ }
+
+ }
+
+ // Pack the arguments into an llvm::array
+ llvm::ArrayRef<lldb_private::ABI::CallArgument> args(rawArgs, numArgs);
+
+ // Setup a thread plan to call the target function
+ lldb::ThreadPlanSP call_plan_sp
+ (
+ new lldb_private::ThreadPlanCallFunctionUsingABI
+ (
+ exe_ctx.GetThreadRef(),
+ funcAddr,
+ *prototype,
+ *returnType,
+ args,
+ options
+ )
+ );
+
+ // Check if the plan is valid
+ if (!call_plan_sp || !call_plan_sp->ValidatePlan(&error_stream))
+ {
+ error.SetErrorToGenericError();
+ error.SetErrorStringWithFormat("unable to make ThreadPlanCallFunctionUsingABI for 0x%llx", I.ULongLong());
+ return false;
+ }
+
+ exe_ctx.GetProcessPtr()->SetRunningUserExpression(true);
+
+ // Execute the actual function call thread plan
+ lldb::ExpressionResults res = exe_ctx.GetProcessRef().RunThreadPlan(exe_ctx, call_plan_sp, options, error_stream);
+
+ // Check that the thread plan completed successfully
+ if (res != lldb::ExpressionResults::eExpressionCompleted)
+ {
+ error.SetErrorToGenericError();
+ error.SetErrorStringWithFormat("ThreadPlanCallFunctionUsingABI failed");
+ return false;
+ }
+
+ exe_ctx.GetProcessPtr()->SetRunningUserExpression(false);
+
+ // Void return type
+ if (returnType->isVoidTy())
+ {
+ // Cant assign to void types, so we leave the frame untouched
+ }
+ else
+ // Integer or pointer return type
+ if (returnType->isIntegerTy() || returnType->isPointerTy())
+ {
+ // Get the encapsulated return value
+ lldb::ValueObjectSP retVal = call_plan_sp.get()->GetReturnValueObject();
+
+ lldb_private::Scalar returnVal = -1;
+ lldb_private::ValueObject *vobj = retVal.get();
+
+ // Check if the return value is valid
+ if (vobj == nullptr || retVal.empty())
+ {
+ error.SetErrorToGenericError();
+ error.SetErrorStringWithFormat("unable to get the return value");
+ return false;
+ }
+
+ // Extract the return value as a integer
+ lldb_private::Value & value = vobj->GetValue();
+ returnVal = value.GetScalar();
+
+ // Push the return value as the result
+ frame.AssignValue(inst, returnVal, module);
+ }
+ }
+ break;
}
++frame.m_ii;
m_allocations.erase(iter);
}
+bool
+IRMemoryMap::GetAllocSize(lldb::addr_t address, size_t &size)
+{
+ AllocationMap::iterator iter = FindAllocation(address, size);
+ if (iter == m_allocations.end())
+ return false;
+
+ Allocation &al = iter->second;
+
+ if (address > (al.m_process_start + al.m_size))
+ {
+ size = 0;
+ return false;
+ }
+
+ if (address > al.m_process_start)
+ {
+ int dif = address - al.m_process_start;
+ size = al.m_size - dif;
+ return true;
+ }
+
+ size = al.m_size;
+ return true;
+}
+
void
IRMemoryMap::WriteMemory (lldb::addr_t process_address, const uint8_t *bytes, size_t size, Error &error)
{
sp -= argSize;
// write this argument onto the stack of the host process
- proc.get( )->WriteMemory( sp, arg.data, arg.size, error );
+ proc.get( )->WriteMemory( sp, arg.data_ap.get(), arg.size, error );
if ( error.Fail( ) )
return false;
addr_t kernel_load_address = SearchForDarwinKernel (process);
if (kernel_load_address != LLDB_INVALID_ADDRESS)
{
- process->SetCanJIT(false);
+ process->SetCanRunCode(false);
return new DynamicLoaderDarwinKernel (process, kernel_load_address);
}
return NULL;
// Disable JIT for hexagon targets because its not supported
m_process->SetCanJIT(false);
+ // Enable Interpreting of function call expressions
+ m_process->SetCanInterpretFunctionCalls(true);
+
// Add the current executable to the module list
ModuleList module_list;
module_list.Append(executable);
ThreadPlan.cpp
ThreadPlanBase.cpp
ThreadPlanCallFunction.cpp
+ ThreadPlanCallFunctionUsingABI.cpp
ThreadPlanCallUserExpression.cpp
ThreadPlanPython.cpp
ThreadPlanRunToAddress.cpp
m_force_next_event_delivery (false),
m_last_broadcast_state (eStateInvalid),
m_destroy_in_process (false),
+ m_can_interpret_function_calls(false),
m_can_jit(eCanJITDontKnow)
{
CheckInWithManager ();
m_can_jit = (can_jit ? eCanJITYes : eCanJITNo);
}
+void
+Process::SetCanRunCode (bool can_run_code)
+{
+ SetCanJIT(can_run_code);
+ m_can_interpret_function_calls = can_run_code;
+}
+
Error
Process::DeallocateMemory (addr_t ptr)
{
m_trap_exceptions (options.GetTrapExceptions()),
m_function_addr (function),
m_function_sp (0),
- m_return_type (return_type),
m_takedown_done (false),
m_should_clear_objc_exception_bp(false),
m_should_clear_cxx_exception_bp (false),
- m_stop_address (LLDB_INVALID_ADDRESS)
+ m_stop_address (LLDB_INVALID_ADDRESS),
+ m_return_type (return_type)
{
- lldb::addr_t start_load_addr;
- ABI *abi;
- lldb::addr_t function_load_addr;
+ lldb::addr_t start_load_addr = LLDB_INVALID_ADDRESS;
+ lldb::addr_t function_load_addr = LLDB_INVALID_ADDRESS;
+ ABI *abi = nullptr;
+
if (!ConstructorSetup (thread, abi, start_load_addr, function_load_addr))
return;
m_valid = true;
}
+ThreadPlanCallFunction::ThreadPlanCallFunction(Thread &thread,
+ const Address &function,
+ const EvaluateExpressionOptions &options) :
+ ThreadPlan(ThreadPlan::eKindCallFunction, "Call function plan", thread, eVoteNoOpinion, eVoteNoOpinion),
+ m_valid(false),
+ m_stop_other_threads(options.GetStopOthers()),
+ m_unwind_on_error(options.DoesUnwindOnError()),
+ m_ignore_breakpoints(options.DoesIgnoreBreakpoints()),
+ m_debug_execution(options.GetDebug()),
+ m_trap_exceptions(options.GetTrapExceptions()),
+ m_function_addr(function),
+ m_function_sp(0),
+ m_takedown_done(false),
+ m_should_clear_objc_exception_bp(false),
+ m_should_clear_cxx_exception_bp(false),
+ m_stop_address(LLDB_INVALID_ADDRESS),
+ m_return_type(ClangASTType())
+{
+
+}
+
ThreadPlanCallFunction::~ThreadPlanCallFunction ()
{
DoTakedown(PlanSucceeded());
{
if (success)
{
- ProcessSP process_sp (m_thread.GetProcess());
- const ABI *abi = process_sp ? process_sp->GetABI().get() : NULL;
- if (abi && m_return_type.IsValid())
- {
- const bool persistent = false;
- m_return_valobj_sp = abi->GetReturnValueObject (m_thread, m_return_type, persistent);
- }
+ SetReturnValue();
}
if (log)
log->Printf ("ThreadPlanCallFunction(%p): DoTakedown called for thread 0x%4.4" PRIx64 ", m_valid: %d complete: %d.\n",
return GetThread().RestoreThreadStateFromCheckpoint(m_stored_thread_state);
}
+
+void
+ThreadPlanCallFunction::SetReturnValue()
+{
+ ProcessSP process_sp(m_thread.GetProcess());
+ const ABI *abi = process_sp ? process_sp->GetABI().get() : NULL;
+ if (abi && m_return_type.IsValid())
+ {
+ const bool persistent = false;
+ m_return_valobj_sp = abi->GetReturnValueObject(m_thread, m_return_type, persistent);
+ }
+}
--- /dev/null
+//===-- ThreadPlanCallFunctionUsingABI.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/Target/ThreadPlanCallFunctionUsingABI.h"
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+
+// Project includes
+#include "lldb/Core/Address.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Core/Stream.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+//--------------------------------------------------------------------------------------------
+// ThreadPlanCallFunctionUsingABI: Plan to call a single function using the ABI instead of JIT
+//-------------------------------------------------------------------------------------------
+ThreadPlanCallFunctionUsingABI::ThreadPlanCallFunctionUsingABI (Thread &thread,
+ const Address &function,
+ llvm::Type &prototype,
+ llvm::Type &return_type,
+ llvm::ArrayRef<ABI::CallArgument> args,
+ const EvaluateExpressionOptions &options) :
+ ThreadPlanCallFunction(thread,function,options),
+ m_return_type(return_type)
+{
+ lldb::addr_t start_load_addr = LLDB_INVALID_ADDRESS;
+ lldb::addr_t function_load_addr = LLDB_INVALID_ADDRESS;
+ ABI *abi = nullptr;
+
+ if (!ConstructorSetup(thread, abi, start_load_addr, function_load_addr))
+ return;
+
+ if (!abi->PrepareTrivialCall(thread,
+ m_function_sp,
+ function_load_addr,
+ start_load_addr,
+ prototype,
+ args))
+ return;
+
+ ReportRegisterState("ABI Function call was set up. Register state was:");
+
+ m_valid = true;
+}
+
+ThreadPlanCallFunctionUsingABI::~ThreadPlanCallFunctionUsingABI()
+{
+
+}
+
+void
+ThreadPlanCallFunctionUsingABI::GetDescription(Stream *s, DescriptionLevel level)
+{
+ if (level == eDescriptionLevelBrief)
+ {
+ s->Printf("Function call thread plan using ABI instead of JIT");
+ }
+ else
+ {
+ TargetSP target_sp(m_thread.CalculateTarget());
+ s->Printf("Thread plan to call 0x%" PRIx64" using ABI instead of JIT", m_function_addr.GetLoadAddress(target_sp.get()));
+ }
+}
+
+void
+ThreadPlanCallFunctionUsingABI::SetReturnValue()
+{
+ ProcessSP process_sp(m_thread.GetProcess());
+ const ABI *abi = process_sp ? process_sp->GetABI().get() : NULL;
+
+ // Ask the abi for the return value
+ if (abi)
+ {
+ const bool persistent = false;
+ m_return_valobj_sp = abi->GetReturnValueObject(m_thread, m_return_type, persistent);
+ }
+}
--- /dev/null
+"""
+Test calling user defined functions using expression evaluation.
+
+Note:
+ LLDBs current first choice of evaluating functions is using the IR interpreter,
+ which is only supported on Hexagon. Otherwise JIT is used for the evaluation.
+
+"""
+
+import unittest2
+import lldb
+import lldbutil
+from lldbtest import *
+
+class ExprCommandCallUserDefinedFunction(TestBase):
+
+ mydir = TestBase.compute_mydir(__file__)
+
+ def setUp(self):
+ # Call super's setUp().
+ TestBase.setUp(self)
+ # Find the line number to break for main.c.
+ self.line = line_number('main.cpp',
+ '// Please test these expressions while stopped at this line:')
+ @skipUnlessDarwin
+ @dsym_test
+ @expectedFailureDarwin("llvm.org/pr20274") # intermittent failure on MacOSX
+ def test_with_dsym(self):
+ """Test return values of user defined function calls."""
+ self.buildDsym()
+ self.call_function()
+
+ @dwarf_test
+ @expectedFailureFreeBSD("llvm.org/pr20274") # intermittent failure
+ def test_with_dwarf(self):
+ """Test return values of user defined function calls."""
+ self.buildDwarf()
+ self.call_functions()
+
+ def call_functions(self):
+ """Test return values of user defined function calls."""
+
+ # Set breakpoint in main and run exe
+ self.runCmd("file a.out", CURRENT_EXECUTABLE_SET)
+ lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=-1, loc_exact=True)
+
+ self.runCmd("run", RUN_SUCCEEDED)
+
+ # Test recursive function call.
+ self.expect("expr fib(5)", substrs = ['$0 = 5'])
+
+ # Test function with more than one paramter
+ self.expect("expr add(4,8)", substrs = ['$1 = 12'])
+
+ # Test nesting function calls in function paramters
+ self.expect("expr add(add(5,2),add(3,4))", substrs = ['$2 = 14'])
+ self.expect("expr add(add(5,2),fib(5))", substrs = ['$3 = 12'])
+
+ # Test function with pointer paramter
+ self.expect("exp stringCompare((const char*) \"Hello world\")", substrs = ['$4 = true'])
+ self.expect("exp stringCompare((const char*) \"Hellworld\")", substrs = ['$5 = false'])
+
+
+if __name__ == '__main__':
+ import atexit
+ lldb.SBDebugger.Initialize()
+ atexit.register(lambda: lldb.SBDebugger.Terminate())
+ unittest2.main()
#include <iostream>
#include <string>
+#include <cstring>
struct Five
{
return my_five;
}
+unsigned int
+fib(unsigned int n)
+{
+ if (n < 2)
+ return n;
+ else
+ return fib(n - 1) + fib(n - 2);
+}
+
+int
+add(int a, int b)
+{
+ return a + b;
+}
+
+bool
+stringCompare(const char *str)
+{
+ if (strcmp( str, "Hello world" ) == 0)
+ return true;
+ else
+ return false;
+}
+
int main (int argc, char const *argv[])
{
std::string str = "Hello world";