|
From: Jeremy F. <je...@go...> - 2005-02-17 01:57:35
|
CVS commit by fitzhardinge:
An initial implementation of thread modelling. This allows valgrind to
obvserve and model the client's use of the native libpthread library,
and use that model to report on usage problems, and feed thread-related
events to tools such as helgrind.
This is definitely work in progress; this code won't do anything terribly
useful yet. You can enable it with --model-pthreads=yes.
A coregrind/vg_pthreadmodel.c 1.1 [GPL (v2+)]
A coregrind/vg_threadmodel.c 1.1 [GPL (v2+)]
A docs/tm-mutexstates.dot 1.1
A docs/tm-threadstates.dot 1.1
M +3 -0 coregrind/Makefile.am 1.108
M +50 -36 coregrind/core.h 1.83
M +5 -16 coregrind/vg_errcontext.c 1.67
M +35 -1 coregrind/vg_intercept.c.base 1.4
M +12 -0 coregrind/vg_main.c 1.250
M +15 -3 coregrind/vg_redir.c 1.5
M +4 -1 coregrind/vg_scheduler.c 1.222
M +4 -1 coregrind/vg_symtab2.c 1.103
--- valgrind/coregrind/Makefile.am #1.107:1.108
@@ -66,4 +66,6 @@
vg_signals.c \
vg_symtab2.c \
+ vg_threadmodel.c \
+ vg_pthreadmodel.c \
vg_redir.c \
vg_dwarf.c \
@@ -131,4 +133,5 @@
vg_intercept.c
vg_inject_so_CFLAGS = $(AM_CFLAGS) -fpic
+vg_inject_so_LDADD = -ldl
vg_inject_so_LDFLAGS = \
-shared \
--- valgrind/coregrind/core.h #1.82:1.83
@@ -278,4 +278,6 @@ extern Bool VG_(clo_trace_symtab);
/* DEBUG: print thread scheduling events? default: NO */
extern Bool VG_(clo_trace_sched);
+/* DEBUG: print pthreads calls? default: NO */
+extern Bool VG_(clo_trace_pthreads);
/* Display gory details for the k'th most popular error. default:
Infinity. */
@@ -304,4 +306,6 @@ extern Bool VG_(clo_show_below_main);
client address space bounds */
extern Bool VG_(clo_pointercheck);
+/* Model the pthread library */
+extern Bool VG_(clo_model_pthreads);
/* Set up the libc freeres wrapper */
@@ -544,41 +548,19 @@ extern Bool VG_(sk_malloc_called_by_sche
/* ---------------------------------------------------------------------
- Exports of vg_libpthread.c
+ Exports of vg_threadmodel.c
------------------------------------------------------------------ */
-/* Replacements for pthread types, shared between vg_libpthread.c and
- vg_scheduler.c. See comment in vg_libpthread.c above the other
- vg_pthread_*_t types for a description of how these are used. */
-
-struct _vg_pthread_fastlock
-{
- long int __vg_status; /* "Free" or "taken" or head of waiting list */
- int __vg_spinlock; /* Used by compare_and_swap emulation. Also,
- adaptive SMP lock stores spin count here. */
-};
-
-typedef struct
-{
- int __vg_m_reserved; /* Reserved for future use */
- int __vg_m_count; /* Depth of recursive locking */
- /*_pthread_descr*/ void* __vg_m_owner; /* Owner thread (if recursive or errcheck) */
- int __vg_m_kind; /* Mutex kind: fast, recursive or errcheck */
- struct _vg_pthread_fastlock __vg_m_lock; /* Underlying fast lock */
-} vg_pthread_mutex_t;
-
-typedef struct
-{
- struct _vg_pthread_fastlock __vg_c_lock; /* Protect against concurrent access */
- /*_pthread_descr*/ void* __vg_c_waiting; /* Threads waiting on this condition */
+extern void VG_(tm_threadcreate)(ThreadId creator, ThreadId tid, Bool detached);
+extern void VG_(tm_threadexit) (ThreadId tid);
+extern void VG_(tm_threadjoin) (ThreadId joiner, ThreadId joinee);
+extern void VG_(tm_switchto) (ThreadId tid);
- // Nb: the following padding removed because it was missing from an
- // earlier glibc, so the size test in the CONVERT macro was failing.
- // --njn
+extern void VG_(tm_mutex_init) (ThreadId tid, Addr mutexp);
+extern void VG_(tm_mutex_destroy)(ThreadId tid, Addr mutexp);
+extern void VG_(tm_mutex_trylock)(ThreadId tid, Addr mutexp);
+extern void VG_(tm_mutex_giveup) (ThreadId tid, Addr mutexp);
+extern void VG_(tm_mutex_acquire)(ThreadId tid, Addr mutexp);
+extern void VG_(tm_mutex_unlock) (ThreadId tid, Addr mutexp);
- // Padding ensures the size is 48 bytes
- /*char __vg_padding[48 - sizeof(struct _vg_pthread_fastlock)
- - sizeof(void*) - sizeof(long long)];
- long long __vg_align;*/
-} vg_pthread_cond_t;
@@ -1039,7 +1021,11 @@ extern ExeContext* VG_(get_ExeContext2)
------------------------------------------------------------------ */
-extern void VG_(load_suppressions) ( void );
+typedef
+ enum {
+ ThreadErr = -1, // Thread error
+ }
+ CoreErrorKind;
-extern void VG_(record_pthread_error) ( ThreadId tid, Char* msg );
+extern void VG_(load_suppressions) ( void );
extern void VG_(show_all_errors) ( void );
@@ -1849,4 +1835,32 @@ extern void VGA_(interrupted_syscall)(Th
/* ---------------------------------------------------------------------
+ Thread modelling
+ ------------------------------------------------------------------ */
+extern void VG_(tm_thread_create) (ThreadId creator, ThreadId tid, Bool detached);
+extern void VG_(tm_thread_exit) (ThreadId tid);
+extern Bool VG_(tm_thread_exists) (ThreadId tid);
+extern void VG_(tm_thread_detach) (ThreadId tid);
+extern void VG_(tm_thread_join) (ThreadId joiner, ThreadId joinee);
+extern void VG_(tm_thread_switchto)(ThreadId tid);
+
+extern void VG_(tm_mutex_init) (ThreadId tid, Addr mutexp);
+extern void VG_(tm_mutex_destroy)(ThreadId tid, Addr mutexp);
+extern void VG_(tm_mutex_trylock)(ThreadId tid, Addr mutexp);
+extern void VG_(tm_mutex_giveup) (ThreadId tid, Addr mutexp);
+extern void VG_(tm_mutex_acquire)(ThreadId tid, Addr mutexp);
+extern void VG_(tm_mutex_tryunlock)(ThreadId tid, Addr mutexp);
+extern void VG_(tm_mutex_unlock) (ThreadId tid, Addr mutexp);
+extern Bool VG_(tm_mutex_exists) (Addr mutexp);
+
+/* ----- pthreads ----- */
+extern void VG_(pthread_init) ();
+extern void VG_(pthread_startfunc_wrapper)(Addr wrapper);
+
+struct vg_pthread_newthread_data {
+ void *(*startfunc)(void *arg);
+ void *arg;
+};
+
+/* ---------------------------------------------------------------------
Finally - autoconf-generated settings
------------------------------------------------------------------ */
--- valgrind/coregrind/vg_errcontext.c #1.66:1.67
@@ -59,9 +59,4 @@ static Supp* is_suppressible_error ( Err
/* Note: it is imperative this doesn't overlap with (0..) at all, as tools
* effectively extend it by defining their own enums in the (0..) range. */
-typedef
- enum {
- PThreadErr = -1, // Pthreading error
- }
- CoreErrorKind;
/* Errors. Extensible (via the 'extra' field). Tools can use a normal
@@ -206,5 +201,5 @@ static Bool eq_Error ( VgRes res, Error*
switch (e1->ekind) {
- case PThreadErr:
+ case ThreadErr:
vg_assert(VG_(needs).core_errors);
if (e1->string == e2->string)
@@ -233,5 +228,5 @@ static void pp_Error ( Error* err, Bool
switch (err->ekind) {
- case PThreadErr:
+ case ThreadErr:
vg_assert(VG_(needs).core_errors);
VG_(message)(Vg_UserMsg, "%s", err->string );
@@ -335,5 +330,5 @@ static void gen_suppression(Error* err)
VG_(printf)(" <insert a suppression name here>\n");
- if (PThreadErr == err->ekind) {
+ if (ThreadErr == err->ekind) {
VG_(printf)(" core:PThread\n");
@@ -522,5 +517,5 @@ void VG_(maybe_record_error) ( ThreadId
/* update `extra', for non-core errors (core ones don't use 'extra') */
- if (VG_(needs).skin_errors && PThreadErr != ekind) {
+ if (VG_(needs).skin_errors && ThreadErr != ekind) {
extra_size = SK_(update_extra)(p);
@@ -601,10 +596,4 @@ Bool VG_(unique_error) ( ThreadId tid, E
/* These are called not from generated code but from the scheduler */
-void VG_(record_pthread_error) ( ThreadId tid, Char* msg )
-{
- if (! VG_(needs).core_errors) return;
- VG_(maybe_record_error)( tid, PThreadErr, /*addr*/0, msg, /*extra*/NULL );
-}
-
void VG_(show_all_errors) ( void )
{
@@ -945,5 +934,5 @@ Bool supp_matches_error(Supp* su, Error*
switch (su->skind) {
case PThreadSupp:
- return (err->ekind == PThreadErr);
+ return (err->ekind == ThreadErr);
default:
if (VG_(needs).skin_errors) {
--- valgrind/coregrind/vg_scheduler.c #1.221:1.222
@@ -662,5 +662,5 @@ VgSchedReturnCode VG_(scheduler) ( Threa
/* nothing */
VG_(set_running)(tid);
- VG_TRACK( thread_run, tid );
+ VG_(tm_thread_switchto)(tid);
/* OK, do some relatively expensive housekeeping stuff */
@@ -757,4 +757,7 @@ VgSchedReturnCode VG_(scheduler) ( Threa
VGP_POPCC(VgpSched);
+ if (VG_(clo_model_pthreads))
+ VG_(tm_thread_exit)(tid);
+
return tst->exitreason;
}
--- valgrind/coregrind/vg_main.c #1.249:1.250
@@ -1450,4 +1450,5 @@ Bool VG_(clo_trace_signals) = False;
Bool VG_(clo_trace_symtab) = False;
Bool VG_(clo_trace_sched) = False;
+Bool VG_(clo_trace_pthreads) = False;
Int VG_(clo_dump_error) = 0;
Int VG_(clo_backtrace_size) = 4;
@@ -1459,4 +1460,5 @@ Bool VG_(clo_show_below_main) = False;
Bool VG_(clo_pointercheck) = True;
Bool VG_(clo_branchpred) = False;
+Bool VG_(clo_model_pthreads) = False;
static Bool VG_(clo_wait_for_gdb) = False;
@@ -1515,4 +1517,5 @@ void usage ( Bool debug_help )
" --trace-sched=no|yes show thread scheduler details? [no]\n"
" --wait-for-gdb=yes|no pause on startup to wait for gdb attach\n"
+" --model-pthreads=yes|no model the pthreads library [no]\n"
"\n"
" debugging options for Valgrind tools that report errors\n"
@@ -1684,5 +1687,7 @@ static void process_cmd_line_options( UI
else VG_BOOL_CLO("--trace-symtab", VG_(clo_trace_symtab))
else VG_BOOL_CLO("--trace-syscalls", VG_(clo_trace_syscalls))
+ else VG_BOOL_CLO("--trace-pthreads", VG_(clo_trace_pthreads))
else VG_BOOL_CLO("--wait-for-gdb", VG_(clo_wait_for_gdb))
+ else VG_BOOL_CLO("--model-pthreads", VG_(clo_model_pthreads))
else VG_STR_CLO ("--db-command", VG_(clo_db_command))
@@ -2569,4 +2574,11 @@ int main(int argc, char **argv, char **e
//--------------------------------------------------------------
+ // Initialise the pthread model
+ // p: ?
+ //--------------------------------------------------------------
+ if (VG_(clo_model_pthreads))
+ VG_(pthread_init)();
+
+ //--------------------------------------------------------------
// Initialise the signal handling subsystem
// p: n/a
--- valgrind/coregrind/vg_redir.c #1.4:1.5
@@ -499,4 +499,6 @@ void VG_(wrap_before)(ThreadState *tst,
Addr argp = (Addr)&ARCH_FUNC_ARG(tst->arch, 0);
void *nonce = NULL;
+ Bool mf = VG_(my_fault);
+ VG_(my_fault) = True;
if (wrapper->before) {
@@ -532,4 +534,6 @@ void VG_(wrap_before)(ThreadState *tst,
} else
vg_assert(nonce == NULL);
+
+ VG_(my_fault) = mf;
}
@@ -540,8 +544,13 @@ void VG_(wrap_after)(ThreadState *tst)
Addr ESP = ARCH_STACK_PTR(tst->arch); /* pointer to args */
Word ret = ARCH_RETVAL(tst->arch); /* return value */
+ struct call_instance *call;
+ Bool mf = VG_(my_fault);
- struct call_instance *call = find_call(EIP, ESP, tst->tid);
+ VG_(my_fault) = True;
+ call = find_call(EIP, ESP, tst->tid);
+ if (0)
VG_(printf)("wrap_after(%p,%p,%d) -> %p\n", EIP, ESP, tst->tid, call);
+
if (call != NULL) {
if (call->wrapper->after)
@@ -551,4 +560,5 @@ void VG_(wrap_after)(ThreadState *tst)
VG_(SkipNode_Free)(&wrapped_frames, call);
}
+ VG_(my_fault) = mf;
}
@@ -608,4 +618,6 @@ Bool VG_(is_wrapper_return)(Addr eip)
}
+/* Mark eip as being the return address of a wrapper, so that the
+ codegen will generate the appropriate call. */
void wrapper_return(Addr eip)
{
--- valgrind/coregrind/vg_symtab2.c #1.102:1.103
@@ -821,5 +821,8 @@ static
void handle_wrapper( SegInfo* si, Char* symbol, Elf32_Sym* sym)
{
+ if (VG_(strcmp)(symbol, STR(VG_WRAPPER(freeres))) == 0)
VGA_(intercept_libc_freeres_wrapper)((Addr)(si->offset + sym->st_value));
+ else if (VG_(strcmp)(symbol, STR(VG_WRAPPER(pthread_startfunc_wrapper))) == 0)
+ VG_(pthread_startfunc_wrapper)((Addr)(si->offset + sym->st_value));
}
--- valgrind/coregrind/vg_intercept.c.base #1.3:1.4
@@ -1,3 +1,3 @@
-
+// -*- c -*-
/*--------------------------------------------------------------------*/
/*--- Intercepts for various libc functions we want to capture ---*/
@@ -69,4 +69,5 @@
#include <unistd.h>
#include <signal.h>
+#include <dlfcn.h>
/* ---------------------------------------------------------------------
@@ -87,4 +88,37 @@
}
+/*
+ The pthread_create wrapper repoints the "start_routine" argument to
+ this function. This function is here calls pthread_self(), which
+ the pthread model will notice and create the mapping from pthread_t
+ to Valgrind's internal ThreadId.
+ */
+void *VG_WRAPPER(pthread_startfunc_wrapper)(void *v)
+{
+ struct vg_pthread_newthread_data *data = (struct vg_pthread_newthread_data *)v;
+ void *(*func)(void *) = data->startfunc;
+ void *arg = data->arg;
+ int ret;
+ static pthread_t (*pthread_selfp)(void);
+
+ //VALGRIND_PRINTF("intercepted thread start: real start is %p(%p)\n", func, arg);
+
+ /* Do this rather than a direct call so we don't make an explicit
+ dependency on libpthread. One presumes that libpthread has
+ already been loaded if we've got to this point though. */
+ if (pthread_selfp == NULL)
+ pthread_selfp = dlsym(NULL, "pthread_self");
+
+ if (pthread_selfp != NULL)
+ (*pthread_selfp)(); /* just calling this is enough */
+
+ /* Free the data the before_pthread_create wrapper left for us. */
+ VALGRIND_MAGIC_SEQUENCE(ret, 0, VG_USERREQ__FREE, data, 0, 0, 0);
+
+ return (*func)(arg);
+
+ /* XXX should we tell core the thread returned? */
+}
+
/*--------------------------------------------------------------------*/
/*--- end vg_intercept.c ---*/
|