Expression evaluation, a new ThreadPlanCallFunctionUsingABI for executing a function call on target via register manipulation

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
This commit is contained in:
Ewan Crawford 2015-07-14 10:56:58 +00:00
parent 469609a714
commit 90ff791141
20 changed files with 637 additions and 54 deletions

View File

@ -44,7 +44,8 @@ public:
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,
@ -53,7 +54,8 @@ public:
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

View File

@ -60,7 +60,7 @@ public:
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();

View File

@ -39,10 +39,9 @@ public:
};
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
@ -58,7 +57,7 @@ public:
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

View File

@ -2471,7 +2471,40 @@ public:
/// 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.
///
@ -3268,6 +3301,7 @@ protected:
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,

View File

@ -34,6 +34,10 @@ public:
llvm::ArrayRef<lldb::addr_t> args,
const EvaluateExpressionOptions &options);
ThreadPlanCallFunction(Thread &thread,
const Address &function,
const EvaluateExpressionOptions &options);
virtual
~ThreadPlanCallFunction ();
@ -134,7 +138,8 @@ protected:
virtual bool
DoPlanExplainsStop (Event *event_ptr);
private:
virtual void
SetReturnValue();
bool
ConstructorSetup (Thread &thread,
@ -153,7 +158,7 @@ private:
bool
BreakpointsExplainStop ();
bool m_valid;
bool m_stop_other_threads;
bool m_unwind_on_error;
@ -172,13 +177,14 @@ private:
// 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);
};

View File

@ -0,0 +1,58 @@
//===-- 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_

View File

@ -2689,6 +2689,8 @@
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>"; };
@ -4522,6 +4524,8 @@
26BC7DEF10F1B80200F91463 /* Target */ = {
isa = PBXGroup;
children = (
EBDD01261B39B66700E04792 /* ThreadPlanCallFunctionUsingABI.h */,
EBDD01241B39B62F00E04792 /* ThreadPlanCallFunctionUsingABI.cpp */,
8CF02AE019DCBF3B00B14BE0 /* InstrumentationRuntime.h */,
8CF02ADF19DCBF3B00B14BE0 /* InstrumentationRuntime.cpp */,
8CF02AEE19DD15CF00B14BE0 /* InstrumentationRuntimeStopInfo.h */,
@ -6315,6 +6319,7 @@
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 */,

View File

@ -541,11 +541,12 @@ ClangExpressionParser::PrepareForExecution (lldb::addr_t &func_addr,
bool ir_can_run = ir_for_target.runOnModule(*execution_unit_sp->GetModule());
Error interpret_error;
can_interpret = IRInterpreter::CanInterpret(*execution_unit_sp->GetModule(), *execution_unit_sp->GetFunction(), interpret_error);
Process *process = exe_ctx.GetProcessPtr();
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);
if (!ir_can_run)
{
err.SetErrorString("The expression could not be prepared to run in the target");

View File

@ -891,7 +891,8 @@ ClangUserExpression::Execute (Stream &error_stream,
*m_execution_unit_sp.get(),
interpreter_error,
function_stack_bottom,
function_stack_top);
function_stack_top,
exe_ctx);
if (!interpreter_error.Success())
{

View File

@ -10,17 +10,28 @@
#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"
@ -455,7 +466,8 @@ static const char *infinite_loop_error = "Interpreter ran for too m
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));
@ -507,7 +519,7 @@ IRInterpreter::CanInterpret (llvm::Module &module,
return false;
}
if (!CanIgnoreCall(call_inst))
if (!CanIgnoreCall(call_inst) && !support_function_calls)
{
if (log)
log->Printf("Unsupported instruction: %s", PrintValue(ii).c_str());
@ -611,7 +623,8 @@ IRInterpreter::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)
{
lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS));
@ -668,29 +681,7 @@ IRInterpreter::Interpret (llvm::Module &module,
{
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:
@ -1476,6 +1467,242 @@ IRInterpreter::Interpret (llvm::Module &module,
}
}
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;

View File

@ -418,6 +418,32 @@ IRMemoryMap::Free (lldb::addr_t process_address, Error &error)
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)
{

View File

@ -257,7 +257,7 @@ ABISysV_hexagon::PrepareTrivialCall ( Thread &thread,
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;

View File

@ -182,7 +182,7 @@ DynamicLoaderDarwinKernel::CreateInstance (Process* process, bool force)
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;

View File

@ -168,6 +168,9 @@ DynamicLoaderHexagonDYLD::DidAttach()
// 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);

View File

@ -40,6 +40,7 @@ add_lldb_library(lldbTarget
ThreadPlan.cpp
ThreadPlanBase.cpp
ThreadPlanCallFunction.cpp
ThreadPlanCallFunctionUsingABI.cpp
ThreadPlanCallUserExpression.cpp
ThreadPlanPython.cpp
ThreadPlanRunToAddress.cpp

View File

@ -754,6 +754,7 @@ Process::Process(Target &target, Listener &listener, const UnixSignalsSP &unix_s
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 ();
@ -3011,6 +3012,13 @@ Process::SetCanJIT (bool can_jit)
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)
{

View File

@ -147,15 +147,16 @@ ThreadPlanCallFunction::ThreadPlanCallFunction (Thread &thread,
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;
@ -171,6 +172,27 @@ ThreadPlanCallFunction::ThreadPlanCallFunction (Thread &thread,
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());
@ -222,13 +244,7 @@ ThreadPlanCallFunction::DoTakedown (bool success)
{
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",
@ -574,3 +590,15 @@ ThreadPlanCallFunction::RestoreThreadState()
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);
}
}

View File

@ -0,0 +1,91 @@
//===-- 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);
}
}

View File

@ -0,0 +1,68 @@
"""
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()

View File

@ -1,5 +1,6 @@
#include <iostream>
#include <string>
#include <cstring>
struct Five
{
@ -14,6 +15,30 @@ returnsFive()
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";