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 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 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(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(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(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 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/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 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 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); +} +