Hi,
We wanted to be able to call functions while debugging for Hexagon however this
is only possible from lldb currently when there is JIT support.
Currently LLDB supports emulation via IR interpreting and executing on target
via JIT however The IR interpretation is limited and cannot currently make
function calls.
We have extended the IR interpreter so that it can execute a function call on
target, via register manipulation. This is the same method
that is used by GDB for calling a function. This involved handling the Call IR
instruction, passing arguments to a new thread plan and collecting any return
values and
passing them back into the IR interpreter.
Several interfaces to the ABI were extended to allow more detailed information
about arguments and return values when setting up a trivial call. See
"ABI::PrepareTrivialCall".
Currently the expression evaluator checks if an expression can be interpreted
and if it can it uses the IR interpreter, if it can't then it tries to JIT the
expression and
run it on target. It seems however that for targets that can JIT, it would be
better to use that when executing a function call since its presumably much
more robust.
Perhaps this should be used exclusively for targets that cannot JIT?
http://reviews.llvm.org/D4672
Files:
include/lldb/Expression/IRInterpreter.h
include/lldb/Expression/IRMemoryMap.h
include/lldb/Target/ThreadPlanCallFunctionGDB.h
source/Expression/ClangUserExpression.cpp
source/Expression/IRInterpreter.cpp
source/Expression/IRMemoryMap.cpp
source/Target/CMakeLists.txt
source/Target/ThreadPlanCallFunction.cpp
source/Target/ThreadPlanCallFunctionGDB.cpp
Index: include/lldb/Expression/IRInterpreter.h
===================================================================
--- include/lldb/Expression/IRInterpreter.h
+++ include/lldb/Expression/IRInterpreter.h
@@ -44,7 +44,8 @@
static bool
CanInterpret (llvm::Module &module,
llvm::Function &function,
- lldb_private::Error &error);
+ lldb_private::Error &error,
+ lldb_private::ExecutionContext &exe_cxt);
static bool
Interpret (llvm::Module &module,
@@ -53,7 +54,8 @@
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 &target);
private:
static bool
Index: include/lldb/Expression/IRMemoryMap.h
===================================================================
--- include/lldb/Expression/IRMemoryMap.h
+++ include/lldb/Expression/IRMemoryMap.h
@@ -69,6 +69,9 @@
// This function can return NULL.
ExecutionContextScope *GetBestExecutionContextScope() const;
+ //
+ bool getAllocSize( lldb::addr_t address, size_t &size );
+
protected:
// This function should only be used if you know you are using the JIT.
// Any other cases should use GetBestExecutionContextScope().
Index: include/lldb/Target/ThreadPlanCallFunctionGDB.h
===================================================================
--- /dev/null
+++ include/lldb/Target/ThreadPlanCallFunctionGDB.h
@@ -0,0 +1,188 @@
+//===-- ThreadPlanCallFunction.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_ThreadPlanCallFunction_h_
+#define liblldb_ThreadPlanCallFunction_h_
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+// Project includes
+#include "lldb/lldb-private.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlan.h"
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/IR/Type.h"
+
+namespace lldb_private {
+
+class ThreadPlanCallFunctionGDB : public ThreadPlan
+{
+ // Create a thread plan to call a function at the address passed in the "function"
+ // argument. If you plan to call GetReturnValueObject, then pass in the
+ // return type, otherwise just pass in an invalid ClangASTType.
+public:
+ ThreadPlanCallFunctionGDB (Thread &thread,
+ const Address &function_address,
+ llvm::Type &function_prototype,
+ llvm::Type &return_type,
+ llvm::ArrayRef<ABI::CallArgument> args,
+ const EvaluateExpressionOptions &options);
+
+ virtual
+ ~ThreadPlanCallFunctionGDB ();
+
+ virtual void
+ GetDescription (Stream *s, lldb::DescriptionLevel level);
+
+ virtual bool
+ ValidatePlan (Stream *error);
+
+ virtual bool
+ ShouldStop (Event *event_ptr);
+
+ virtual Vote
+ ShouldReportStop(Event *event_ptr);
+
+ virtual bool
+ StopOthers ();
+
+ virtual lldb::StateType
+ GetPlanRunState ();
+
+ virtual void
+ DidPush ();
+
+ virtual bool
+ WillStop ();
+
+ virtual bool
+ MischiefManaged ();
+
+ // To get the return value from a function call you must create a
+ // lldb::ValueSP that contains a valid clang type in its context and call
+ // RequestReturnValue. The ValueSP will be stored and when the function is
+ // done executing, the object will check if there is a requested return
+ // value. If there is, the return value will be retrieved using the
+ // ABI::GetReturnValue() for the ABI in the process. Then after the thread
+ // plan is complete, you can call "GetReturnValue()" to retrieve the value
+ // that was extracted.
+
+ virtual lldb::ValueObjectSP
+ GetReturnValueObject ()
+ {
+ return m_return_valobj_sp;
+ }
+
+ // Return the stack pointer that the function received
+ // on entry. Any stack address below this should be
+ // considered invalid after the function has been
+ // cleaned up.
+ lldb::addr_t
+ GetFunctionStackPointer()
+ {
+ return m_function_sp;
+ }
+
+ // Classes that derive from ClangFunction, and implement
+ // their own WillPop methods should call this so that the
+ // thread state gets restored if the plan gets discarded.
+ virtual void
+ WillPop ();
+
+ // If the thread plan stops mid-course, this will be the stop reason that interrupted us.
+ // Once DoTakedown is called, this will be the real stop reason at the end of the function call.
+ // If it hasn't been set for one or the other of these reasons, we'll return the PrivateStopReason.
+ // This is needed because we want the CallFunction thread plans not to show up as the stop reason.
+ // But if something bad goes wrong, it is nice to be able to tell the user what really happened.
+
+ virtual lldb::StopInfoSP
+ GetRealStopInfo()
+ {
+ if (m_real_stop_info_sp)
+ return m_real_stop_info_sp;
+ else
+ return GetPrivateStopInfo ();
+ }
+
+ lldb::addr_t
+ GetStopAddress ()
+ {
+ return m_stop_address;
+ }
+
+ virtual bool
+ RestoreThreadState();
+
+ virtual void
+ ThreadDestroyed ()
+ {
+ m_takedown_done = true;
+ }
+
+protected:
+ void ReportRegisterState (const char *message);
+
+ virtual bool
+ DoPlanExplainsStop (Event *event_ptr);
+
+private:
+
+ bool
+ ConstructorSetup (Thread &thread,
+ ABI *& abi,
+ lldb::addr_t &start_load_addr,
+ lldb::addr_t &function_load_addr);
+
+ void
+ DoTakedown (bool success);
+
+ void
+ SetBreakpoints ();
+
+ void
+ ClearBreakpoints ();
+
+ bool
+ BreakpointsExplainStop ();
+
+ bool m_valid;
+ bool m_stop_other_threads;
+ bool m_unwind_on_error;
+ bool m_ignore_breakpoints;
+ bool m_debug_execution;
+ bool m_trap_exceptions;
+ Address m_function_addr;
+ Address m_start_addr;
+ lldb::addr_t m_function_sp;
+ lldb::ThreadPlanSP m_subplan_sp;
+ LanguageRuntime *m_cxx_language_runtime;
+ LanguageRuntime *m_objc_language_runtime;
+ Thread::ThreadStateCheckpoint m_stored_thread_state;
+ lldb::StopInfoSP m_real_stop_info_sp; // In general we want to hide call function
+ // thread plans, but for reporting purposes,
+ // it's nice to know the real stop reason.
+ // This gets set in DoTakedown.
+ StreamString m_constructor_errors;
+ llvm::Type &m_return_type;
+ llvm::Type &m_function_prototype;
+
+ 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;
+
+ DISALLOW_COPY_AND_ASSIGN (ThreadPlanCallFunctionGDB);
+};
+
+} // namespace lldb_private
+
+#endif // liblldb_ThreadPlanCallFunction_h_
Index: source/Expression/ClangUserExpression.cpp
===================================================================
--- source/Expression/ClangUserExpression.cpp
+++ source/Expression/ClangUserExpression.cpp
@@ -857,7 +857,9 @@
*m_execution_unit_sp.get(),
interpreter_error,
function_stack_bottom,
- function_stack_top);
+ function_stack_top,
+ exe_ctx
+ );
if (!interpreter_error.Success())
{
Index: source/Expression/IRInterpreter.cpp
===================================================================
--- source/Expression/IRInterpreter.cpp
+++ source/Expression/IRInterpreter.cpp
@@ -16,6 +16,12 @@
#include "lldb/Expression/IRInterpreter.h"
#include "lldb/Host/Endian.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlan.h"
+#include "lldb/Target/ThreadPlanCallFunctionGDB.h"
+
+#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/Function.h"
@@ -24,6 +30,13 @@
#include "llvm/IR/Module.h"
#include "llvm/Support/raw_ostream.h"
+#include "lldb/Target/ExecutionContext.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ValueObject.h"
+
+#include "lldb/Target/ABI.h"
+
#include <map>
using namespace llvm;
@@ -455,7 +468,8 @@
bool
IRInterpreter::CanInterpret (llvm::Module &module,
llvm::Function &function,
- lldb_private::Error &error)
+ lldb_private::Error &error,
+ lldb_private::ExecutionContext &exe_cxt)
{
lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS));
@@ -497,7 +511,7 @@
case Instruction::Br:
break;
case Instruction::Call:
- {
+ {
CallInst *call_inst = dyn_cast<CallInst>(ii);
if (!call_inst)
@@ -506,6 +520,10 @@
error.SetErrorString(interpreter_internal_error);
return false;
}
+ // For the time being, don't mess up other targets with hexagon stuff.
+ const lldb_private::ArchSpec &spec = exe_cxt.GetTargetRef().GetArchitecture();
+ if (spec.GetCore() == lldb_private::ArchSpec::eCore_hexagon_generic)
+ break;
if (!CanIgnoreCall(call_inst))
{
@@ -515,6 +533,7 @@
error.SetErrorString(unsupported_opcode_error);
return false;
}
+
}
break;
case Instruction::GetElementPtr:
@@ -602,7 +621,8 @@
}
- return true;}
+ return true;
+}
bool
IRInterpreter::Interpret (llvm::Module &module,
@@ -611,7 +631,8 @@
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_cxt)
{
lldb_private::Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS));
@@ -668,29 +689,7 @@
{
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 +1475,256 @@
}
}
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;
+ }
+
+ llvm::LLVMContext &llvmCtx = call_inst->getContext( );
+
+ // 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;
+ }
+
+ // 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
+ 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 int32 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 );
+ llvm::Type *str = arg_ty->getContainedType( 0 );
+
+ uint32_t addr = tmp_op.ULongLong( );
+ size_t dataSize = 0;
+
+ if ( memory_map.getAllocSize( addr, dataSize ) )
+ {
+#pragma message ("This is currently a memory leak and needs a proper solution")
+ // create the required buffer
+ rawArgs[i].size = dataSize;
+ rawArgs[i].data = new uint8_t[dataSize + 1];
+ // read string from host memory
+ memory_map.ReadMemory( rawArgs[i].data, addr, dataSize, error );
+ if ( error.Fail( ) )
+ {
+ assert( ! "we have failed to read the string from memory" );
+ return false;
+ }
+ // add null terminator
+ rawArgs[i].data[dataSize] = '\0';
+ //
+ rawArgs[i].type =
+ lldb_private::ABI::CallArgument::HostPointer;
+
+#if HEX_DEBUG
+ //!! print for debugging
+ printf( "string is: '%s'", rawArgs[i].data );
+#endif
+ }
+ 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;
+ // 4 bytes for 32bit integer
+ rawArgs[i].size = 4;
+ // 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 );
+
+ // check we can actually get a thread
+ if ( exe_cxt.GetThreadPtr() == nullptr )
+ {
+ error.SetErrorToGenericError();
+ error.SetErrorStringWithFormat ( "unable to acquire thread" );
+ return false;
+ }
+
+ // setup a thread plan to call the target function
+ lldb::ThreadPlanSP call_plan_sp
+ (
+ new lldb_private::ThreadPlanCallFunctionGDB
+ (
+ exe_cxt.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 ThreadPlanCallFunctionGDB for 0x%08x", I.ULongLong() );
+ return false;
+ }
+
+ // make sure we have a valid process
+ if (! exe_cxt.GetProcessPtr())
+ {
+ error.SetErrorToGenericError();
+ error.SetErrorStringWithFormat ( "unable to get the process" );
+ return false;
+ }
+
+ exe_cxt.GetProcessPtr()->SetRunningUserExpression (true);
+
+ // execute the actual function call thread plan
+ lldb::ExpressionResults res =
+ exe_cxt.GetProcessRef().RunThreadPlan (exe_cxt, call_plan_sp, options, error_stream);
+
+ // check that the thread plan completed successfully
+ if ( res != lldb::ExpressionResults::eExpressionCompleted )
+ {
+ error.SetErrorToGenericError();
+ error.SetErrorStringWithFormat ( "threadPlanCallFunctionGDB failed" );
+ return false;
+ }
+
+ exe_cxt.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);
+ }
+ }
}
++frame.m_ii;
Index: source/Expression/IRMemoryMap.cpp
===================================================================
--- source/Expression/IRMemoryMap.cpp
+++ source/Expression/IRMemoryMap.cpp
@@ -418,6 +418,32 @@
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)
{
Index: source/Target/ThreadPlanCallFunction.cpp
===================================================================
--- source/Target/ThreadPlanCallFunction.cpp
+++ source/Target/ThreadPlanCallFunction.cpp
@@ -153,9 +153,10 @@
m_should_clear_cxx_exception_bp (false),
m_stop_address (LLDB_INVALID_ADDRESS)
{
- 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;
Index: source/Target/ThreadPlanCallFunctionGDB.cpp
===================================================================
--- /dev/null
+++ source/Target/ThreadPlanCallFunctionGDB.cpp
@@ -0,0 +1,551 @@
+//===-- ThreadPlanCallFunctionGDB.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/ThreadPlanCallFunctionGDB.h"
+
+// C Includes
+// C++ Includes
+// Other libraries and framework includes
+#include "llvm/Support/MachO.h"
+// Project includes
+#include "lldb/lldb-private-log.h"
+#include "lldb/Breakpoint/Breakpoint.h"
+#include "lldb/Breakpoint/BreakpointLocation.h"
+#include "lldb/Core/Address.h"
+#include "lldb/Core/Log.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/Stream.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/LanguageRuntime.h"
+#include "lldb/Target/Process.h"
+#include "lldb/Target/RegisterContext.h"
+#include "lldb/Target/StopInfo.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Target/Thread.h"
+#include "lldb/Target/ThreadPlanRunToAddress.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+//----------------------------------------------------------------------
+// ThreadPlanCallFunctionGDB: Plan to call a single function
+//----------------------------------------------------------------------
+bool
+ThreadPlanCallFunctionGDB::ConstructorSetup (Thread &thread,
+ ABI *& abi,
+ lldb::addr_t &start_load_addr,
+ lldb::addr_t &function_load_addr)
+{
+ SetIsMasterPlan (true);
+ SetOkayToDiscard (false);
+ SetPrivate (true);
+
+ ProcessSP process_sp (thread.GetProcess());
+ if (!process_sp)
+ return false;
+
+ abi = process_sp->GetABI().get();
+
+ if (!abi)
+ return false;
+
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_STEP));
+
+ SetBreakpoints();
+
+ m_function_sp = thread.GetRegisterContext()->GetSP() - abi->GetRedZoneSize();
+ // If we can't read memory at the point of the process where we are planning to put our function, we're
+ // not going to get any further...
+ Error error;
+ process_sp->ReadUnsignedIntegerFromMemory(m_function_sp, 4, 0, error);
+ if (!error.Success())
+ {
+ m_constructor_errors.Printf ("Trying to put the stack in unreadable memory at: 0x%" PRIx64 ".", m_function_sp);
+ if (log)
+ log->Printf ("ThreadPlanCallFunctionGDB(%p): %s.", this, m_constructor_errors.GetData());
+ return false;
+ }
+
+ Module *exe_module = GetTarget().GetExecutableModulePointer();
+
+ if (exe_module == NULL)
+ {
+ m_constructor_errors.Printf ("Can't execute code without an executable module.");
+ if (log)
+ log->Printf ("ThreadPlanCallFunctionGDB(%p): %s.", this, m_constructor_errors.GetData());
+ return false;
+ }
+ else
+ {
+ ObjectFile *objectFile = exe_module->GetObjectFile();
+ if (!objectFile)
+ {
+ m_constructor_errors.Printf ("Could not find object file for module \"%s\".",
+ exe_module->GetFileSpec().GetFilename().AsCString());
+
+ if (log)
+ log->Printf ("ThreadPlanCallFunctionGDB(%p): %s.", this, m_constructor_errors.GetData());
+ return false;
+ }
+
+ m_start_addr = objectFile->GetEntryPointAddress();
+ if (!m_start_addr.IsValid())
+ {
+ m_constructor_errors.Printf ("Could not find entry point address for executable module \"%s\".",
+ exe_module->GetFileSpec().GetFilename().AsCString());
+ if (log)
+ log->Printf ("ThreadPlanCallFunctionGDB(%p): %s.", this, m_constructor_errors.GetData());
+ return false;
+ }
+ }
+
+ start_load_addr = m_start_addr.GetLoadAddress (&GetTarget());
+
+ // Checkpoint the thread state so we can restore it later.
+ if (log && log->GetVerbose())
+ ReportRegisterState ("About to checkpoint thread before function call. Original register state was:");
+
+ if (!thread.CheckpointThreadState (m_stored_thread_state))
+ {
+ m_constructor_errors.Printf ("Setting up ThreadPlanCallFunctionGDB, failed to checkpoint thread state.");
+ if (log)
+ log->Printf ("ThreadPlanCallFunctionGDB(%p): %s.", this, m_constructor_errors.GetData());
+ return false;
+ }
+ function_load_addr = m_function_addr.GetLoadAddress (&GetTarget());
+
+ return true;
+}
+
+ThreadPlanCallFunctionGDB::ThreadPlanCallFunctionGDB (
+ Thread &thread,
+ const Address &function,
+ llvm::Type &prototype,
+ llvm::Type &return_type,
+ llvm::ArrayRef<ABI::CallArgument> args,
+ 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_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_function_prototype( prototype )
+{
+ 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 ("Function call was set up. Register state was:");
+
+ m_valid = true;
+}
+
+ThreadPlanCallFunctionGDB::~ThreadPlanCallFunctionGDB ()
+{
+ DoTakedown(PlanSucceeded());
+}
+
+void
+ThreadPlanCallFunctionGDB::ReportRegisterState (const char *message)
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP | LIBLLDB_LOG_VERBOSE));
+ if (log)
+ {
+ StreamString strm;
+ RegisterContext *reg_ctx = m_thread.GetRegisterContext().get();
+
+ log->PutCString(message);
+
+ RegisterValue reg_value;
+
+ for (uint32_t reg_idx = 0, num_registers = reg_ctx->GetRegisterCount();
+ reg_idx < num_registers;
+ ++reg_idx)
+ {
+ const RegisterInfo *reg_info = reg_ctx->GetRegisterInfoAtIndex (reg_idx);
+ if (reg_ctx->ReadRegister(reg_info, reg_value))
+ {
+ reg_value.Dump(&strm, reg_info, true, false, eFormatDefault);
+ strm.EOL();
+ }
+ }
+ log->PutCString(strm.GetData());
+ }
+}
+
+void
+ThreadPlanCallFunctionGDB::DoTakedown (bool success)
+{
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_STEP));
+
+ if (!m_valid)
+ {
+ //Don't call DoTakedown if we were never valid to begin with.
+ if (log)
+ log->Printf ("ThreadPlanCallFunctionGDB(%p): Log called on ThreadPlanCallFunctionGDB that was never valid.", this);
+ return;
+ }
+
+ if (!m_takedown_done)
+ {
+ if (success)
+ {
+ ProcessSP process_sp (m_thread.GetProcess());
+ const ABI *abi = process_sp ? process_sp->GetABI().get() : NULL;
+
+ // ask the abi for the return type
+ if ( abi )
+ {
+ const bool persistent = false;
+ m_return_valobj_sp = abi->GetReturnValueObject (m_thread, m_return_type, persistent);
+ }
+ }
+ if (log)
+ log->Printf ("ThreadPlanCallFunctionGDB(%p): DoTakedown called for thread 0x%4.4" PRIx64 ", m_valid: %d complete: %d.\n", this, m_thread.GetID(), m_valid, IsPlanComplete());
+ m_takedown_done = true;
+ m_stop_address = m_thread.GetStackFrameAtIndex(0)->GetRegisterContext()->GetPC();
+ m_real_stop_info_sp = GetPrivateStopInfo ();
+ if (!m_thread.RestoreRegisterStateFromCheckpoint(m_stored_thread_state))
+ {
+ if (log)
+ log->Printf("ThreadPlanCallFunctionGDB(%p): DoTakedown failed to restore register state", this);
+ }
+ SetPlanComplete(success);
+ ClearBreakpoints();
+ if (log && log->GetVerbose())
+ ReportRegisterState ("Restoring thread state after function call. Restored register state:");
+
+ }
+ else
+ {
+ if (log)
+ log->Printf ("ThreadPlanCallFunctionGDB(%p): DoTakedown called as no-op for thread 0x%4.4" PRIx64 ", m_valid: %d complete: %d.\n", this, m_thread.GetID(), m_valid, IsPlanComplete());
+ }
+}
+
+void
+ThreadPlanCallFunctionGDB::WillPop ()
+{
+ DoTakedown(PlanSucceeded());
+}
+
+void
+ThreadPlanCallFunctionGDB::GetDescription (Stream *s, DescriptionLevel level)
+{
+ if (level == eDescriptionLevelBrief)
+ {
+ s->Printf("Function call thread plan");
+ }
+ else
+ {
+ TargetSP target_sp (m_thread.CalculateTarget());
+ s->Printf("Thread plan to call 0x%" PRIx64, m_function_addr.GetLoadAddress(target_sp.get()));
+ }
+}
+
+bool
+ThreadPlanCallFunctionGDB::ValidatePlan (Stream *error)
+{
+ if (!m_valid)
+ {
+ if (error)
+ {
+ if (m_constructor_errors.GetSize() > 0)
+ error->PutCString (m_constructor_errors.GetData());
+ else
+ error->PutCString ("Unknown error");
+ }
+ return false;
+ }
+
+ return true;
+}
+
+
+Vote
+ThreadPlanCallFunctionGDB::ShouldReportStop(Event *event_ptr)
+{
+ if (m_takedown_done || IsPlanComplete())
+ return eVoteYes;
+ else
+ return ThreadPlan::ShouldReportStop(event_ptr);
+}
+
+bool
+ThreadPlanCallFunctionGDB::DoPlanExplainsStop (Event *event_ptr)
+{
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_STEP|LIBLLDB_LOG_PROCESS));
+ m_real_stop_info_sp = GetPrivateStopInfo ();
+
+ // If our subplan knows why we stopped, even if it's done (which would forward the question to us)
+ // we answer yes.
+ if (m_subplan_sp.get() != NULL && m_subplan_sp->PlanExplainsStop(event_ptr))
+ {
+ SetPlanComplete();
+ return true;
+ }
+
+ // Check if the breakpoint is one of ours.
+
+ StopReason stop_reason;
+ if (!m_real_stop_info_sp)
+ stop_reason = eStopReasonNone;
+ else
+ stop_reason = m_real_stop_info_sp->GetStopReason();
+ if (log)
+ log->Printf ("ThreadPlanCallFunctionGDB::PlanExplainsStop: Got stop reason - %s.", Thread::StopReasonAsCString(stop_reason));
+
+ if (stop_reason == eStopReasonBreakpoint && BreakpointsExplainStop())
+ return true;
+
+ // We control breakpoints separately from other "stop reasons." So first,
+ // check the case where we stopped for an internal breakpoint, in that case, continue on.
+ // If it is not an internal breakpoint, consult m_ignore_breakpoints.
+
+
+ if (stop_reason == eStopReasonBreakpoint)
+ {
+ ProcessSP process_sp (m_thread.CalculateProcess());
+ uint64_t break_site_id = m_real_stop_info_sp->GetValue();
+ BreakpointSiteSP bp_site_sp;
+ if (process_sp)
+ bp_site_sp = process_sp->GetBreakpointSiteList().FindByID(break_site_id);
+ if (bp_site_sp)
+ {
+ uint32_t num_owners = bp_site_sp->GetNumberOfOwners();
+ bool is_internal = true;
+ for (uint32_t i = 0; i < num_owners; i++)
+ {
+ Breakpoint &bp = bp_site_sp->GetOwnerAtIndex(i)->GetBreakpoint();
+ if (log)
+ log->Printf ("ThreadPlanCallFunctionGDB::PlanExplainsStop: hit breakpoint %d while calling function", bp.GetID());
+
+ if (!bp.IsInternal())
+ {
+ is_internal = false;
+ break;
+ }
+ }
+ if (is_internal)
+ {
+ if (log)
+ log->Printf ("ThreadPlanCallFunctionGDB::PlanExplainsStop hit an internal breakpoint, not stopping.");
+ return false;
+ }
+ }
+
+ if (m_ignore_breakpoints)
+ {
+ if (log)
+ log->Printf("ThreadPlanCallFunctionGDB::PlanExplainsStop: we are ignoring breakpoints, overriding breakpoint stop info ShouldStop, returning true");
+ m_real_stop_info_sp->OverrideShouldStop(false);
+ return true;
+ }
+ else
+ {
+ if (log)
+ log->Printf("ThreadPlanCallFunctionGDB::PlanExplainsStop: we are not ignoring breakpoints, overriding breakpoint stop info ShouldStop, returning true");
+ m_real_stop_info_sp->OverrideShouldStop(true);
+ return false;
+ }
+ }
+ else if (!m_unwind_on_error)
+ {
+ // If we don't want to discard this plan, than any stop we don't understand should be propagated up the stack.
+ return false;
+ }
+ else
+ {
+ // If the subplan is running, any crashes are attributable to us.
+ // If we want to discard the plan, then we say we explain the stop
+ // but if we are going to be discarded, let whoever is above us
+ // explain the stop.
+ // But don't discard the plan if the stop would restart itself (for instance if it is a
+ // signal that is set not to stop. Check that here first. We just say we explain the stop
+ // but aren't done and everything will continue on from there.
+
+ if (m_real_stop_info_sp->ShouldStopSynchronous(event_ptr))
+ {
+ SetPlanComplete(false);
+ if (m_subplan_sp)
+ {
+ if (m_unwind_on_error)
+ return true;
+ else
+ return false;
+ }
+ else
+ return false;
+ }
+ else
+ return true;
+ }
+}
+
+bool
+ThreadPlanCallFunctionGDB::ShouldStop (Event *event_ptr)
+{
+ // We do some computation in DoPlanExplainsStop that may or may not set the plan as complete.
+ // We need to do that here to make sure our state is correct.
+ DoPlanExplainsStop(event_ptr);
+
+ if (IsPlanComplete())
+ {
+ ReportRegisterState ("Function completed. Register state was:");
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+bool
+ThreadPlanCallFunctionGDB::StopOthers ()
+{
+ return m_stop_other_threads;
+}
+
+StateType
+ThreadPlanCallFunctionGDB::GetPlanRunState ()
+{
+ return eStateRunning;
+}
+
+void
+ThreadPlanCallFunctionGDB::DidPush ()
+{
+//#define SINGLE_STEP_EXPRESSIONS
+
+ // Now set the thread state to "no reason" so we don't run with whatever signal was outstanding...
+ // Wait till the plan is pushed so we aren't changing the stop info till we're about to run.
+
+ GetThread().SetStopInfoToNothing();
+
+#ifndef SINGLE_STEP_EXPRESSIONS
+ m_subplan_sp.reset(new ThreadPlanRunToAddress(m_thread, m_start_addr, m_stop_other_threads));
+
+ m_thread.QueueThreadPlan(m_subplan_sp, false);
+ m_subplan_sp->SetPrivate (true);
+#endif
+}
+
+bool
+ThreadPlanCallFunctionGDB::WillStop ()
+{
+ return true;
+}
+
+bool
+ThreadPlanCallFunctionGDB::MischiefManaged ()
+{
+ Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
+
+ if (IsPlanComplete())
+ {
+ if (log)
+ log->Printf("ThreadPlanCallFunctionGDB(%p): Completed call function plan.", this);
+
+ ThreadPlan::MischiefManaged ();
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+void
+ThreadPlanCallFunctionGDB::SetBreakpoints ()
+{
+ ProcessSP process_sp (m_thread.CalculateProcess());
+ if (m_trap_exceptions && process_sp)
+ {
+ m_cxx_language_runtime = process_sp->GetLanguageRuntime(eLanguageTypeC_plus_plus);
+ m_objc_language_runtime = process_sp->GetLanguageRuntime(eLanguageTypeObjC);
+
+ if (m_cxx_language_runtime)
+ {
+ m_should_clear_cxx_exception_bp = !m_cxx_language_runtime->ExceptionBreakpointsAreSet();
+ m_cxx_language_runtime->SetExceptionBreakpoints();
+ }
+ if (m_objc_language_runtime)
+ {
+ m_should_clear_objc_exception_bp = !m_objc_language_runtime->ExceptionBreakpointsAreSet();
+ m_objc_language_runtime->SetExceptionBreakpoints();
+ }
+ }
+}
+
+void
+ThreadPlanCallFunctionGDB::ClearBreakpoints ()
+{
+ if (m_trap_exceptions)
+ {
+ if (m_cxx_language_runtime && m_should_clear_cxx_exception_bp)
+ m_cxx_language_runtime->ClearExceptionBreakpoints();
+ if (m_objc_language_runtime && m_should_clear_objc_exception_bp)
+ m_objc_language_runtime->ClearExceptionBreakpoints();
+ }
+}
+
+bool
+ThreadPlanCallFunctionGDB::BreakpointsExplainStop()
+{
+ StopInfoSP stop_info_sp = GetPrivateStopInfo ();
+
+ if (m_trap_exceptions)
+ {
+ if ((m_cxx_language_runtime &&
+ m_cxx_language_runtime->ExceptionBreakpointsExplainStop(stop_info_sp))
+ ||(m_objc_language_runtime &&
+ m_objc_language_runtime->ExceptionBreakpointsExplainStop(stop_info_sp)))
+ {
+ Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_STEP));
+ if (log)
+ log->Printf ("ThreadPlanCallFunctionGDB::BreakpointsExplainStop - Hit an exception breakpoint, setting plan complete.");
+
+ SetPlanComplete(false);
+
+ // If the user has set the ObjC language breakpoint, it would normally get priority over our internal
+ // catcher breakpoint, but in this case we can't let that happen, so force the ShouldStop here.
+ stop_info_sp->OverrideShouldStop (true);
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool
+ThreadPlanCallFunctionGDB::RestoreThreadState()
+{
+ return GetThread().RestoreThreadStateFromCheckpoint(m_stored_thread_state);
+}
+
Index: source/Target/CMakeLists.txt
===================================================================
--- source/Target/CMakeLists.txt
+++ source/Target/CMakeLists.txt
@@ -37,6 +37,7 @@
ThreadPlan.cpp
ThreadPlanBase.cpp
ThreadPlanCallFunction.cpp
+ ThreadPlanCallFunctionGDB.cpp
ThreadPlanCallUserExpression.cpp
ThreadPlanRunToAddress.cpp
ThreadPlanShouldStopHere.cpp
_______________________________________________
lldb-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/lldb-commits