Add a new SBThread::SafeToCallFunctions API; this calls over to

the SystemRuntime to check if a thread will have any problems 
performing an inferior function call so the driver can skip
making that function call on that thread.  Often the function
call can be executed on another thread instead.
<rdar://problem/16777874> 

llvm-svn: 208732
This commit is contained in:
Jason Molenda 2014-05-13 22:02:48 +00:00
parent 1b91aa2cf5
commit b4892cd266
15 changed files with 261 additions and 0 deletions

View File

@ -210,6 +210,9 @@ public:
uint32_t uint32_t
GetExtendedBacktraceOriginatingIndexID (); GetExtendedBacktraceOriginatingIndexID ();
bool
SafeToCallFunctions ();
protected: protected:
friend class SBBreakpoint; friend class SBBreakpoint;
friend class SBBreakpointLocation; friend class SBBreakpointLocation;

View File

@ -309,6 +309,27 @@ public:
{ {
} }
//------------------------------------------------------------------
/// Determine whether it is safe to run an expression on a given thread
///
/// If a system must not run functions on a thread in some particular state,
/// this method gives a way for it to flag that the expression should not be
/// run.
///
/// @param [in] thread_sp
/// The thread we want to run the expression on.
///
/// @return
/// True will be returned if there are no known problems with running an
/// expression on this thread. False means that the inferior function
/// call should not be made on this thread.
//------------------------------------------------------------------
virtual bool
SafeToCallFunctionsOnThisThread (lldb::ThreadSP thread_sp)
{
return true;
}
protected: protected:
//------------------------------------------------------------------ //------------------------------------------------------------------
// Member variables. // Member variables.

View File

@ -592,6 +592,19 @@ public:
virtual lldb::addr_t virtual lldb::addr_t
GetThreadLocalData (const lldb::ModuleSP module); GetThreadLocalData (const lldb::ModuleSP module);
//------------------------------------------------------------------
/// Check whether this thread is safe to run functions
///
/// The SystemRuntime may know of certain thread states (functions in
/// process of execution, for instance) which can make it unsafe for
/// functions to be called.
///
/// @return
/// True if it is safe to call functions on this thread.
/// False if function calls should be avoided on this thread.
//------------------------------------------------------------------
virtual bool
SafeToCallFunctions ();
//------------------------------------------------------------------ //------------------------------------------------------------------
// Thread Plan Providers: // Thread Plan Providers:

View File

@ -297,6 +297,16 @@ public:
uint32_t uint32_t
GetExtendedBacktraceOriginatingIndexID(); GetExtendedBacktraceOriginatingIndexID();
%feature("autodoc","
Takes no arguments, returns a bool.
lldb may be able to detect that function calls should not be executed
on a given thread at a particular point in time. It is recommended that
this is checked before performing an inferior function call on a given
thread.
") SafeToCallFunctions;
bool
SafeToCallFunctions ();
%pythoncode %{ %pythoncode %{
class frames_access(object): class frames_access(object):
'''A helper object that will lazily hand out frames for a thread when supplied an index.''' '''A helper object that will lazily hand out frames for a thread when supplied an index.'''

View File

@ -1437,3 +1437,12 @@ SBThread::GetExtendedBacktraceOriginatingIndexID ()
return thread_sp->GetExtendedBacktraceOriginatingIndexID(); return thread_sp->GetExtendedBacktraceOriginatingIndexID();
return LLDB_INVALID_INDEX32; return LLDB_INVALID_INDEX32;
} }
bool
SBThread::SafeToCallFunctions ()
{
ThreadSP thread_sp(m_opaque_sp->GetThreadSP());
if (thread_sp)
return thread_sp->SafeToCallFunctions();
return true;
}

View File

@ -269,6 +269,14 @@ AppleGetItemInfoHandler::GetItemInfo (Thread &thread, uint64_t item, addr_t page
error.Clear(); error.Clear();
if (thread.SafeToCallFunctions() == false)
{
if (log)
log->Printf ("Not safe to call functions on thread 0x%" PRIx64, thread.GetID());
error.SetErrorString ("Not safe to call functions on this thread.");
return return_value;
}
// Set up the arguments for a call to // Set up the arguments for a call to
// struct get_item_info_return_values // struct get_item_info_return_values

View File

@ -274,6 +274,14 @@ AppleGetPendingItemsHandler::GetPendingItems (Thread &thread, addr_t queue, addr
error.Clear(); error.Clear();
if (thread.SafeToCallFunctions() == false)
{
if (log)
log->Printf ("Not safe to call functions on thread 0x%" PRIx64, thread.GetID());
error.SetErrorString ("Not safe to call functions on this thread.");
return return_value;
}
// Set up the arguments for a call to // Set up the arguments for a call to
// struct get_pending_items_return_values // struct get_pending_items_return_values

View File

@ -282,6 +282,14 @@ AppleGetQueuesHandler::GetCurrentQueues (Thread &thread, addr_t page_to_free, ui
error.Clear(); error.Clear();
if (thread.SafeToCallFunctions() == false)
{
if (log)
log->Printf ("Not safe to call functions on thread 0x%" PRIx64, thread.GetID());
error.SetErrorString ("Not safe to call functions on this thread.");
return return_value;
}
// Set up the arguments for a call to // Set up the arguments for a call to
// struct get_current_queues_return_values // struct get_current_queues_return_values

View File

@ -273,6 +273,14 @@ AppleGetThreadItemInfoHandler::GetThreadItemInfo (Thread &thread, tid_t thread_i
error.Clear(); error.Clear();
if (thread.SafeToCallFunctions() == false)
{
if (log)
log->Printf ("Not safe to call functions on thread 0x%" PRIx64, thread.GetID());
error.SetErrorString ("Not safe to call functions on this thread.");
return return_value;
}
// Set up the arguments for a call to // Set up the arguments for a call to
// struct get_thread_item_info_return_values // struct get_thread_item_info_return_values

View File

@ -214,6 +214,21 @@ SystemRuntimeMacOSX::GetQueueKind (addr_t dispatch_queue_addr)
return kind; return kind;
} }
bool
SystemRuntimeMacOSX::SafeToCallFunctionsOnThisThread (ThreadSP thread_sp)
{
if (thread_sp && thread_sp->GetStackFrameCount() > 0 && thread_sp->GetFrameWithConcreteFrameIndex(0))
{
const SymbolContext sym_ctx (thread_sp->GetFrameWithConcreteFrameIndex(0)->GetSymbolContext (eSymbolContextSymbol));
static ConstString g_select_symbol ("__select");
if (sym_ctx.GetFunctionName() == g_select_symbol)
{
return false;
}
}
return true;
}
lldb::queue_id_t lldb::queue_id_t
SystemRuntimeMacOSX::GetQueueIDFromThreadQAddress (lldb::addr_t dispatch_qaddr) SystemRuntimeMacOSX::GetQueueIDFromThreadQAddress (lldb::addr_t dispatch_qaddr)
{ {

View File

@ -108,6 +108,9 @@ public:
virtual lldb::QueueKind virtual lldb::QueueKind
GetQueueKind (lldb::addr_t dispatch_queue_addr); GetQueueKind (lldb::addr_t dispatch_queue_addr);
virtual bool
SafeToCallFunctionsOnThisThread (lldb::ThreadSP thread_sp);
//------------------------------------------------------------------ //------------------------------------------------------------------
// PluginInterface protocol // PluginInterface protocol
//------------------------------------------------------------------ //------------------------------------------------------------------

View File

@ -26,6 +26,7 @@
#include "lldb/Target/Process.h" #include "lldb/Target/Process.h"
#include "lldb/Target/RegisterContext.h" #include "lldb/Target/RegisterContext.h"
#include "lldb/Target/StopInfo.h" #include "lldb/Target/StopInfo.h"
#include "lldb/Target/SystemRuntime.h"
#include "lldb/Target/Target.h" #include "lldb/Target/Target.h"
#include "lldb/Target/Thread.h" #include "lldb/Target/Thread.h"
#include "lldb/Target/ThreadPlan.h" #include "lldb/Target/ThreadPlan.h"
@ -1953,6 +1954,21 @@ Thread::GetThreadLocalData (const ModuleSP module)
return LLDB_INVALID_ADDRESS; return LLDB_INVALID_ADDRESS;
} }
bool
Thread::SafeToCallFunctions ()
{
Process *process = GetProcess().get();
if (process)
{
SystemRuntime *runtime = process->GetSystemRuntime ();
if (runtime)
{
return runtime->SafeToCallFunctionsOnThisThread (shared_from_this());
}
}
return true;
}
lldb::StackFrameSP lldb::StackFrameSP
Thread::GetStackFrameSPForStackFramePtr (StackFrame *stack_frame_ptr) Thread::GetStackFrameSPForStackFramePtr (StackFrame *stack_frame_ptr)
{ {

View File

@ -0,0 +1,28 @@
CC ?= clang
ifeq "$(ARCH)" ""
ARCH = x86_64
endif
ifeq "$(OS)" ""
OS = $(shell uname -s)
endif
CFLAGS ?= -g -O0
CWD := $(shell pwd)
LIB_PREFIX := lib
ifeq "$(OS)" "Darwin"
CFLAGS += -arch $(ARCH)
endif
all: a.out
a.out: main.o
$(CC) $(CFLAGS) -o a.out main.o
main.o: main.c
$(CC) $(CFLAGS) -c main.c
clean:
rm -rf *.o *~ *.dylib *.so a.out *.dSYM

View File

@ -0,0 +1,81 @@
"""Test function call thread safety."""
import os, time
import unittest2
import lldb
import lldbutil
from lldbtest import *
class TestSafeFuncCalls(TestBase):
mydir = TestBase.compute_mydir(__file__)
@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
@python_api_test
@dsym_test
def test_with_dsym_and_python_api(self):
"""Test function call thread safety."""
self.buildDsym()
self.function_call_safety_check()
@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
@python_api_test
@dwarf_test
def test_with_dwarf_and_python_api(self):
"""Test function call thread safety."""
self.buildDwarf()
self.function_call_safety_check()
def setUp(self):
# Call super's setUp().
TestBase.setUp(self)
# Find the line numbers that we will step to in main:
self.main_source = "main.c"
def check_number_of_threads(self, process):
self.assertTrue(process.GetNumThreads() == 2, "Check that the process has two threads when sitting at the stopper() breakpoint")
def safe_to_call_func_on_main_thread (self, main_thread):
self.assertTrue(main_thread.SafeToCallFunctions() == True, "It is safe to call functions on the main thread")
def safe_to_call_func_on_select_thread (self, select_thread):
self.assertTrue(select_thread.SafeToCallFunctions() == False, "It is not safe to call functions on the select thread")
def function_call_safety_check(self):
"""Test function call safety checks"""
exe = os.path.join(os.getcwd(), "a.out")
target = self.dbg.CreateTarget(exe)
self.assertTrue(target, VALID_TARGET)
self.main_source_spec = lldb.SBFileSpec (self.main_source)
break1 = target.BreakpointCreateByName ("stopper", 'a.out')
self.assertTrue(break1, VALID_BREAKPOINT)
process = target.LaunchSimple (None, None, self.get_process_working_directory())
self.assertTrue(process, PROCESS_IS_VALID)
threads = lldbutil.get_threads_stopped_at_breakpoint (process, break1)
if len(threads) != 1:
self.fail ("Failed to stop at breakpoint 1.")
self.check_number_of_threads(process)
main_thread = lldb.SBThread()
select_thread = lldb.SBThread()
for idx in range (0, process.GetNumThreads()):
t = process.GetThreadAtIndex (idx)
if t.GetName() == "main thread":
main_thread = t
if t.GetName() == "select thread":
select_thread = t
self.assertTrue(main_thread.IsValid() and select_thread.IsValid(), "Got both expected threads")
self.safe_to_call_func_on_main_thread (main_thread)
self.safe_to_call_func_on_select_thread (select_thread)
if __name__ == '__main__':
import atexit
lldb.SBDebugger.Initialize()
atexit.register(lambda: lldb.SBDebugger.Terminate())
unittest2.main()

View File

@ -0,0 +1,30 @@
#include <sys/select.h>
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
void *
select_thread (void *in)
{
pthread_setname_np ("select thread");
fd_set fdset;
FD_SET (STDIN_FILENO, &fdset);
while (1)
select (2, &fdset, NULL, NULL, NULL);
return NULL;
}
void stopper ()
{
while (1)
sleep(1); // break here
}
int main ()
{
pthread_setname_np ("main thread");
pthread_t other_thread;
pthread_create (&other_thread, NULL, select_thread, NULL);
sleep (1);
stopper();
}