We sometimes need to be able to call functions (via Process::RunThreadPlan) from code run on the private state thread. To do that we have to

spin up a temporary "private state thread" that will respond to events from the lower level process plugins.  This check-in should work to do
that, but it is still buggy.  However, if you don't call functions on the private state thread, these changes make no difference.

This patch also moves the code in the AppleObjCRuntime step-through-trampoline handler that might call functions (in the case where the debug
server doesn't support the memory allocate/deallocate packet) out to a safe place to do that call.

llvm-svn: 154230
This commit is contained in:
Jim Ingham 2012-04-07 00:00:41 +00:00
parent e84f5ca2ea
commit 372787fc19
10 changed files with 298 additions and 135 deletions

View File

@ -205,6 +205,16 @@ public:
static lldb::tid_t
GetCurrentThreadID ();
//------------------------------------------------------------------
/// Get the thread token (the one returned by ThreadCreate when the thread was created) for the
/// calling thread in the current process.
///
/// @return
/// The thread token for the calling thread in the current process.
//------------------------------------------------------------------
static lldb::thread_t
GetCurrentThread ();
static const char *
GetSignalAsCString (int signo);

View File

@ -3143,7 +3143,27 @@ public:
//------------------------------------------------------------------
bool
RemoveInvalidMemoryRange (const LoadRange &region);
//------------------------------------------------------------------
// If the setup code of a thread plan needs to do work that might involve
// calling a function in the target, it should not do that work directly
// in one of the thread plan functions (DidPush/WillResume) because
// such work needs to be handled carefully. Instead, put that work in
// a PreResumeAction callback, and register it with the process. It will
// get done before the actual "DoResume" gets called.
//------------------------------------------------------------------
typedef bool (PreResumeActionCallback)(void *);
void
AddPreResumeAction (PreResumeActionCallback callback, void *baton);
bool
RunPreResumeActions ();
void
ClearPreResumeActions ();
ReadWriteLock &
GetRunLock ()
{
@ -3227,7 +3247,7 @@ protected:
bool
PrivateStateThreadIsValid () const
{
return m_private_state_thread != LLDB_INVALID_HOST_THREAD;
return IS_VALID_LLDB_HOST_THREAD(m_private_state_thread);
}
//------------------------------------------------------------------
@ -3270,6 +3290,19 @@ protected:
bool m_should_detach; /// Should we detach if the process object goes away with an explicit call to Kill or Detach?
LanguageRuntimeCollection m_language_runtimes;
std::auto_ptr<NextEventAction> m_next_event_action_ap;
struct PreResumeCallbackAndBaton
{
bool (*callback) (void *);
void *baton;
PreResumeCallbackAndBaton (PreResumeActionCallback in_callback, void *in_baton) :
callback (in_callback),
baton (in_baton)
{
}
};
std::vector<PreResumeCallbackAndBaton> m_pre_resume_actions;
ReadWriteLock m_run_lock;
enum {
@ -3291,7 +3324,7 @@ protected:
SetPrivateState (lldb::StateType state);
bool
StartPrivateStateThread ();
StartPrivateStateThread (bool force = false);
void
StopPrivateStateThread ();

View File

@ -91,10 +91,7 @@
ignoresPersistentStateOnLaunch = "YES"
debugDocumentVersioning = "YES"
allowLocationSimulation = "YES">
<PathRunnable
FilePath = "/System/Library/Frameworks/Python.framework/Versions/2.7/Resources/Python.app/Contents/MacOS/Python">
</PathRunnable>
<MacroExpansion>
<BuildableProductRunnable>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "26F5C26910F3D9A4009D5894"
@ -102,7 +99,7 @@
BlueprintName = "lldb-tool"
ReferencedContainer = "container:lldb.xcodeproj">
</BuildableReference>
</MacroExpansion>
</BuildableProductRunnable>
<EnvironmentVariables>
<EnvironmentVariable
key = "LLDB_LAUNCH_FLAG_DISABLE_ASLR"

View File

@ -442,6 +442,12 @@ Host::GetCurrentThreadID()
#endif
}
lldb::thread_t
Host::GetCurrentThread ()
{
return lldb::thread_t(pthread_self());
}
const char *
Host::GetSignalAsCString (int signo)
{

View File

@ -582,6 +582,113 @@ AppleObjCTrampolineHandler::AppleObjCTrampolineHandler (const ProcessSP &process
m_vtables_ap->ReadRegions();
}
lldb::addr_t
AppleObjCTrampolineHandler::SetupDispatchFunction (Thread &thread, ValueList &dispatch_values)
{
ExecutionContext exe_ctx (thread.shared_from_this());
Address impl_code_address;
StreamString errors;
LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP));
lldb::addr_t args_addr = LLDB_INVALID_ADDRESS;
// Scope for mutex locker:
{
Mutex::Locker locker(m_impl_function_mutex);
// First stage is to make the ClangUtility to hold our injected function:
#define USE_BUILTIN_FUNCTION 0 // Define this to 1 and we will use the get_implementation function found in the target.
// This is useful for debugging additions to the get_impl function 'cause you don't have
// to bother with string-ifying the code into g_lookup_implementation_function_code.
if (USE_BUILTIN_FUNCTION)
{
ConstString our_utility_function_name("__lldb_objc_find_implementation_for_selector");
SymbolContextList sc_list;
exe_ctx.GetTargetRef().GetImages().FindSymbolsWithNameAndType (our_utility_function_name, eSymbolTypeCode, sc_list);
if (sc_list.GetSize() == 1)
{
SymbolContext sc;
sc_list.GetContextAtIndex(0, sc);
if (sc.symbol != NULL)
impl_code_address = sc.symbol->GetAddress();
//lldb::addr_t addr = impl_code_address.GetOpcodeLoadAddress (exe_ctx.GetTargetPtr());
//printf ("Getting address for our_utility_function: 0x%llx.\n", addr);
}
else
{
//printf ("Could not find implementation function address.\n");
return args_addr;
}
}
else if (!m_impl_code.get())
{
m_impl_code.reset (new ClangUtilityFunction (g_lookup_implementation_function_code,
g_lookup_implementation_function_name));
if (!m_impl_code->Install(errors, exe_ctx))
{
if (log)
log->Printf ("Failed to install implementation lookup: %s.", errors.GetData());
m_impl_code.reset();
return args_addr;
}
impl_code_address.Clear();
impl_code_address.SetOffset(m_impl_code->StartAddress());
}
else
{
impl_code_address.Clear();
impl_code_address.SetOffset(m_impl_code->StartAddress());
}
// Next make the runner function for our implementation utility function.
if (!m_impl_function.get())
{
ClangASTContext *clang_ast_context = thread.GetProcess()->GetTarget().GetScratchClangASTContext();
lldb::clang_type_t clang_void_ptr_type = clang_ast_context->GetVoidPtrType(false);
m_impl_function.reset(new ClangFunction (thread,
clang_ast_context,
clang_void_ptr_type,
impl_code_address,
dispatch_values));
errors.Clear();
unsigned num_errors = m_impl_function->CompileFunction(errors);
if (num_errors)
{
if (log)
log->Printf ("Error compiling function: \"%s\".", errors.GetData());
return args_addr;
}
errors.Clear();
if (!m_impl_function->WriteFunctionWrapper(exe_ctx, errors))
{
if (log)
log->Printf ("Error Inserting function: \"%s\".", errors.GetData());
return args_addr;
}
}
}
errors.Clear();
// Now write down the argument values for this particular call. This looks like it might be a race condition
// if other threads were calling into here, but actually it isn't because we allocate a new args structure for
// this call by passing args_addr = LLDB_INVALID_ADDRESS...
if (!m_impl_function->WriteFunctionArguments (exe_ctx, args_addr, impl_code_address, dispatch_values, errors))
{
if (log)
log->Printf ("Error writing function arguments: \"%s\".", errors.GetData());
return args_addr;
}
return args_addr;
}
ThreadPlanSP
AppleObjCTrampolineHandler::GetStepThroughDispatchPlan (Thread &thread, bool stop_others)
{
@ -864,104 +971,13 @@ AppleObjCTrampolineHandler::GetStepThroughDispatchPlan (Thread &thread, bool sto
else
flag_value.GetScalar() = 0; // FIXME - Set to 0 when debugging is done.
dispatch_values.PushValue (flag_value);
// Now, if we haven't already, make and insert the function as a ClangUtilityFunction, and make and insert
// it's runner ClangFunction.
{
// Scope for mutex locker:
Mutex::Locker locker(m_impl_function_mutex);
// First stage is to make the ClangUtility to hold our injected function:
#define USE_BUILTIN_FUNCTION 0 // Define this to 1 and we will use the get_implementation function found in the target.
// This is useful for debugging additions to the get_impl function 'cause you don't have
// to bother with string-ifying the code into g_lookup_implementation_function_code.
if (USE_BUILTIN_FUNCTION)
{
ConstString our_utility_function_name("__lldb_objc_find_implementation_for_selector");
SymbolContextList sc_list;
exe_ctx.GetTargetRef().GetImages().FindSymbolsWithNameAndType (our_utility_function_name, eSymbolTypeCode, sc_list);
if (sc_list.GetSize() == 1)
{
SymbolContext sc;
sc_list.GetContextAtIndex(0, sc);
if (sc.symbol != NULL)
impl_code_address = sc.symbol->GetAddress();
//lldb::addr_t addr = impl_code_address.GetOpcodeLoadAddress (exe_ctx.GetTargetPtr());
//printf ("Getting address for our_utility_function: 0x%llx.\n", addr);
}
else
{
//printf ("Could not find implementation function address.\n");
return ret_plan_sp;
}
}
else if (!m_impl_code.get())
{
m_impl_code.reset (new ClangUtilityFunction (g_lookup_implementation_function_code,
g_lookup_implementation_function_name));
if (!m_impl_code->Install(errors, exe_ctx))
{
if (log)
log->Printf ("Failed to install implementation lookup: %s.", errors.GetData());
m_impl_code.reset();
return ret_plan_sp;
}
impl_code_address.Clear();
impl_code_address.SetOffset(m_impl_code->StartAddress());
}
else
{
impl_code_address.Clear();
impl_code_address.SetOffset(m_impl_code->StartAddress());
}
// Next make the runner function for our implementation utility function.
if (!m_impl_function.get())
{
m_impl_function.reset(new ClangFunction (thread,
clang_ast_context,
clang_void_ptr_type,
impl_code_address,
dispatch_values));
errors.Clear();
unsigned num_errors = m_impl_function->CompileFunction(errors);
if (num_errors)
{
if (log)
log->Printf ("Error compiling function: \"%s\".", errors.GetData());
return ret_plan_sp;
}
errors.Clear();
if (!m_impl_function->WriteFunctionWrapper(exe_ctx, errors))
{
if (log)
log->Printf ("Error Inserting function: \"%s\".", errors.GetData());
return ret_plan_sp;
}
}
}
errors.Clear();
// Now write down the argument values for this particular call. This looks like it might be a race condition
// if other threads were calling into here, but actually it isn't because we allocate a new args structure for
// this call by passing args_addr = LLDB_INVALID_ADDRESS...
lldb::addr_t args_addr = LLDB_INVALID_ADDRESS;
if (!m_impl_function->WriteFunctionArguments (exe_ctx, args_addr, impl_code_address, dispatch_values, errors))
return ret_plan_sp;
ret_plan_sp.reset (new AppleThreadPlanStepThroughObjCTrampoline (thread, this, args_addr,
dispatch_values.GetValueAtIndex(0)->GetScalar().ULongLong(),
isa_addr, sel_addr,
stop_others));
ret_plan_sp.reset (new AppleThreadPlanStepThroughObjCTrampoline (thread,
this,
dispatch_values,
isa_addr,
sel_addr,
stop_others));
if (log)
{
StreamString s;

View File

@ -60,7 +60,10 @@ public:
bool is_super2;
FixUpState fixedup;
};
lldb::addr_t
SetupDispatchFunction (Thread &thread, ValueList &dispatch_values);
private:
static const char *g_lookup_implementation_function_name;
static const char *g_lookup_implementation_function_code;

View File

@ -33,9 +33,8 @@ using namespace lldb_private;
AppleThreadPlanStepThroughObjCTrampoline::AppleThreadPlanStepThroughObjCTrampoline
(
Thread &thread,
AppleObjCTrampolineHandler *trampoline_handler,
lldb::addr_t args_addr,
lldb::addr_t object_addr,
AppleObjCTrampolineHandler *trampoline_handler,
ValueList &input_values,
lldb::addr_t isa_addr,
lldb::addr_t sel_addr,
bool stop_others
@ -46,11 +45,11 @@ AppleThreadPlanStepThroughObjCTrampoline::AppleThreadPlanStepThroughObjCTrampoli
eVoteNoOpinion,
eVoteNoOpinion),
m_trampoline_handler (trampoline_handler),
m_args_addr (args_addr),
m_object_addr (object_addr),
m_args_addr (LLDB_INVALID_ADDRESS),
m_input_values (input_values),
m_isa_addr(isa_addr),
m_sel_addr(sel_addr),
m_impl_function (trampoline_handler->GetLookupImplementationWrapperFunction()),
m_impl_function (NULL),
m_stop_others (stop_others)
{
@ -66,12 +65,42 @@ AppleThreadPlanStepThroughObjCTrampoline::~AppleThreadPlanStepThroughObjCTrampol
void
AppleThreadPlanStepThroughObjCTrampoline::DidPush ()
{
StreamString errors;
ExecutionContext exc_ctx;
m_thread.CalculateExecutionContext(exc_ctx);
m_func_sp.reset(m_impl_function->GetThreadPlanToCallFunction (exc_ctx, m_args_addr, errors, m_stop_others));
m_func_sp->SetPrivate(true);
m_thread.QueueThreadPlan (m_func_sp, false);
// StreamString errors;
// ExecutionContext exc_ctx;
// m_thread.CalculateExecutionContext(exc_ctx);
// m_func_sp.reset(m_impl_function->GetThreadPlanToCallFunction (exc_ctx, m_args_addr, errors, m_stop_others));
// m_func_sp->SetPrivate(true);
// m_thread.QueueThreadPlan (m_func_sp, false);
m_thread.GetProcess()->AddPreResumeAction (PreResumeInitializeClangFunction, (void *) this);
}
bool
AppleThreadPlanStepThroughObjCTrampoline::InitializeClangFunction ()
{
if (!m_func_sp)
{
StreamString errors;
m_args_addr = m_trampoline_handler->SetupDispatchFunction(m_thread, m_input_values);
if (m_args_addr == LLDB_INVALID_ADDRESS)
{
return false;
}
m_impl_function = m_trampoline_handler->GetLookupImplementationWrapperFunction();
ExecutionContext exc_ctx;
m_thread.CalculateExecutionContext(exc_ctx);
m_func_sp.reset(m_impl_function->GetThreadPlanToCallFunction (exc_ctx, m_args_addr, errors, m_stop_others));
m_func_sp->SetPrivate(true);
m_thread.QueueThreadPlan (m_func_sp, false);
}
return true;
}
bool
AppleThreadPlanStepThroughObjCTrampoline::PreResumeInitializeClangFunction(void *void_myself)
{
AppleThreadPlanStepThroughObjCTrampoline *myself = static_cast<AppleThreadPlanStepThroughObjCTrampoline *>(void_myself);
return myself->InitializeClangFunction();
}
void
@ -83,7 +112,7 @@ AppleThreadPlanStepThroughObjCTrampoline::GetDescription (Stream *s,
else
{
s->Printf ("Stepping to implementation of ObjC method - obj: 0x%llx, isa: 0x%llx, sel: 0x%llx",
m_object_addr, m_isa_addr, m_sel_addr);
m_input_values.GetValueAtIndex(0)->GetScalar().ULongLong(), m_isa_addr, m_sel_addr);
}
}

View File

@ -16,6 +16,7 @@
// Project includes
#include "lldb/lldb-types.h"
#include "lldb/lldb-enumerations.h"
#include "lldb/Core/Value.h"
#include "lldb/Target/ThreadPlan.h"
#include "AppleObjCTrampolineHandler.h"
@ -30,8 +31,7 @@ public:
//------------------------------------------------------------------
AppleThreadPlanStepThroughObjCTrampoline(Thread &thread,
AppleObjCTrampolineHandler *trampoline_handler,
lldb::addr_t args_addr,
lldb::addr_t object_addr,
ValueList &values,
lldb::addr_t isa_addr,
lldb::addr_t sel_addr,
bool stop_others);
@ -63,6 +63,9 @@ public:
virtual void
DidPush();
static bool
PreResumeInitializeClangFunction(void *myself);
virtual bool
WillStop();
@ -74,12 +77,16 @@ protected:
//------------------------------------------------------------------
private:
bool
InitializeClangFunction ();
//------------------------------------------------------------------
// For AppleThreadPlanStepThroughObjCTrampoline only
//------------------------------------------------------------------
AppleObjCTrampolineHandler *m_trampoline_handler; // FIXME - ensure this doesn't go away on us? SP maybe?
lldb::addr_t m_args_addr; // Stores the address for our step through function result structure.
lldb::addr_t m_object_addr; // This is only for Description.
//lldb::addr_t m_object_addr; // This is only for Description.
ValueList m_input_values;
lldb::addr_t m_isa_addr; // isa_addr and sel_addr are the keys we will use to cache the implementation.
lldb::addr_t m_sel_addr;
lldb::ThreadPlanSP m_func_sp; // This is the function call plan. We fill it at start, then set it

View File

@ -312,8 +312,10 @@ public:
SetCurrentThreadForRun (int tid);
lldb_private::LazyBool
SupportsAllocDeallocMemory () const
SupportsAllocDeallocMemory () // const
{
// Uncomment this to have lldb pretend the debug server doesn't respond to alloc/dealloc memory packets.
// m_supports_alloc_dealloc_memory = lldb_private::eLazyBoolNo;
return m_supports_alloc_dealloc_memory;
}

View File

@ -2793,14 +2793,22 @@ Process::Resume ()
// to see if they are suppoed to start back up with a signal.
if (m_thread_list.WillResume())
{
m_mod_id.BumpResumeID();
error = DoResume();
if (error.Success())
// Last thing, do the PreResumeActions.
if (!RunPreResumeActions())
{
DidResume();
m_thread_list.DidResume();
if (log)
log->Printf ("Process thinks the process has resumed.");
error.SetErrorStringWithFormat ("Process::Resume PreResumeActions failed, not resuming.");
}
else
{
m_mod_id.BumpResumeID();
error = DoResume();
if (error.Success())
{
DidResume();
m_thread_list.DidResume();
if (log)
log->Printf ("Process thinks the process has resumed.");
}
}
}
else
@ -3074,7 +3082,7 @@ Process::ShouldBroadcastEvent (Event *event_ptr)
bool
Process::StartPrivateStateThread ()
Process::StartPrivateStateThread (bool force)
{
LogSP log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EVENTS));
@ -3082,13 +3090,16 @@ Process::StartPrivateStateThread ()
if (log)
log->Printf ("Process::%s()%s ", __FUNCTION__, already_running ? " already running" : " starting private state thread");
if (already_running)
if (!force && already_running)
return true;
// Create a thread that watches our internal state and controls which
// events make it to clients (into the DCProcess event queue).
char thread_name[1024];
snprintf(thread_name, sizeof(thread_name), "<lldb.process.internal-state(pid=%llu)>", GetID());
if (already_running)
snprintf(thread_name, sizeof(thread_name), "<lldb.process.internal-state-override(pid=%llu)>", GetID());
else
snprintf(thread_name, sizeof(thread_name), "<lldb.process.internal-state(pid=%llu)>", GetID());
m_private_state_thread = Host::ThreadCreate (thread_name, Process::PrivateStateThread, this, NULL);
return IS_VALID_LLDB_HOST_THREAD(m_private_state_thread);
}
@ -3834,7 +3845,7 @@ Process::UpdateInstanceName ()
ExecutionResults
Process::RunThreadPlan (ExecutionContext &exe_ctx,
lldb::ThreadPlanSP &thread_plan_sp,
lldb::ThreadPlanSP &thread_plan_sp,
bool stop_others,
bool try_all_threads,
bool discard_on_error,
@ -3896,7 +3907,20 @@ Process::RunThreadPlan (ExecutionContext &exe_ctx,
selected_tid = LLDB_INVALID_THREAD_ID;
}
thread->QueueThreadPlan(thread_plan_sp, true);
lldb::thread_t backup_private_state_thread = LLDB_INVALID_HOST_THREAD;
if (Host::GetCurrentThread() == m_private_state_thread)
{
// Yikes, we are running on the private state thread! So we can't call DoRunThreadPlan on this thread, since
// then nobody will be around to fetch internal events.
// The simplest thing to do is to spin up a temporary thread to handle private state thread events while
// we are doing the RunThreadPlan here.
backup_private_state_thread = m_private_state_thread;
printf ("Running thread plan on private state thread, spinning up another state thread to handle the events.\n");
StartPrivateStateThread(true);
}
thread->QueueThreadPlan(thread_plan_sp, false); // This used to pass "true" does that make sense?
Listener listener("lldb.process.listener.run-thread-plan");
@ -4253,6 +4277,17 @@ Process::RunThreadPlan (ExecutionContext &exe_ctx,
} // END WAIT LOOP
// If we had to start up a temporary private state thread to run this thread plan, shut it down now.
if (IS_VALID_LLDB_HOST_THREAD(backup_private_state_thread))
{
StopPrivateStateThread();
lldb::thread_result_t thread_result;
Error error;
// Host::ThreadJoin(m_private_state_thread, &thread_result, &error);
m_private_state_thread = backup_private_state_thread;
}
// Now do some processing on the results of the run:
if (return_value == eExecutionInterrupted)
{
@ -4506,6 +4541,31 @@ Process::RemoveInvalidMemoryRange (const LoadRange &region)
return m_memory_cache.RemoveInvalidRange(region.GetRangeBase(), region.GetByteSize());
}
void
Process::AddPreResumeAction (PreResumeActionCallback callback, void *baton)
{
m_pre_resume_actions.push_back(PreResumeCallbackAndBaton (callback, baton));
}
bool
Process::RunPreResumeActions ()
{
bool result = true;
while (!m_pre_resume_actions.empty())
{
struct PreResumeCallbackAndBaton action = m_pre_resume_actions.back();
m_pre_resume_actions.pop_back();
bool this_result = action.callback (action.baton);
if (result == true) result = this_result;
}
return result;
}
void
Process::ClearPreResumeActions ()
{
m_pre_resume_actions.clear();
}
//--------------------------------------------------------------
// class Process::SettingsController