From: Julian Seward Date: Thu, 18 Apr 2002 11:37:52 +0000 (+0000) Subject: Yay! Opera (6.0TP2) now works. X-Git-Tag: svn/VALGRIND_1_0_3~353 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f6f515f0b8ca7be1e7d8b88b88f6ffb3bca257ca;p=thirdparty%2Fvalgrind.git Yay! Opera (6.0TP2) now works. Various enhancements: * Make the error message system more thread-aware. * Fix stupid bug in do_pthread_create causing incorrect initial %ESP values sometimes. * Fix various other minor things needed to make opera work. Performance of threaded apps is pretty terrible. This needs looking into. git-svn-id: svn://svn.valgrind.org/valgrind/trunk@98 --- diff --git a/coregrind/arch/x86-linux/vg_libpthread.c b/coregrind/arch/x86-linux/vg_libpthread.c index f32c5533ea..8ee3ccb10e 100644 --- a/coregrind/arch/x86-linux/vg_libpthread.c +++ b/coregrind/arch/x86-linux/vg_libpthread.c @@ -499,6 +499,13 @@ int send(int s, const void *msg, size_t len, int flags) return __libc_send(s, msg, len, flags); } +extern +int __libc_recv(int s, void *buf, size_t len, int flags); +int recv(int s, void *buf, size_t len, int flags) +{ + return __libc_recv(s, buf, len, flags); +} + /*--------------------------------------------------*/ diff --git a/coregrind/vg_clientmalloc.c b/coregrind/vg_clientmalloc.c index dca0e3e5cb..b1848405aa 100644 --- a/coregrind/vg_clientmalloc.c +++ b/coregrind/vg_clientmalloc.c @@ -529,15 +529,17 @@ void VG_(describe_addr) ( Addr a, AddrInfo* ai ) ShadowChunk* sc; UInt ml_no; Bool ok; + ThreadId tid; /* Perhaps it's a user-def'd block ? */ ok = VG_(client_perm_maybe_describe)( a, ai ); if (ok) return; - /* Perhaps it's on the stack? */ - if (VG_(is_plausible_stack_addr)(a) - && a >= (Addr)VG_(baseBlock)[VGOFF_(m_esp)]) { - ai->akind = Stack; + /* Perhaps it's on a thread's stack? */ + tid = VG_(identify_stack_addr)(a); + if (tid != VG_INVALID_THREADID) { + ai->akind = Stack; + ai->stack_tid = tid; return; } /* Search for a freed block which might bracket it. */ diff --git a/coregrind/vg_errcontext.c b/coregrind/vg_errcontext.c index c632124eaa..0ac2cb36b4 100644 --- a/coregrind/vg_errcontext.c +++ b/coregrind/vg_errcontext.c @@ -142,6 +142,8 @@ typedef Char* syscall_param; /* Param, User */ Bool isWriteableLack; + /* ALL */ + ThreadId tid; } ErrContext; @@ -174,6 +176,7 @@ static void clear_AddrInfo ( AddrInfo* ai ) ai->blksize = 0; ai->rwoffset = 0; ai->lastchange = NULL; + ai->stack_tid = VG_INVALID_THREADID; } static void clear_ErrContext ( ErrContext* ec ) @@ -189,6 +192,7 @@ static void clear_ErrContext ( ErrContext* ec ) clear_AddrInfo ( &ec->addrinfo ); ec->syscall_param = NULL; ec->isWriteableLack = False; + ec->tid = VG_INVALID_THREADID; } @@ -264,7 +268,9 @@ static void pp_AddrInfo ( Addr a, AddrInfo* ai ) { switch (ai->akind) { case Stack: - VG_(message)(Vg_UserMsg, " Address 0x%x is on the stack", a); + VG_(message)(Vg_UserMsg, + " Address 0x%x is on thread %d's stack", + ai->stack_tid, a); break; case Unknown: VG_(message)(Vg_UserMsg, @@ -309,6 +315,8 @@ static void pp_ErrContext ( ErrContext* ec, Bool printCount ) { if (printCount) VG_(message)(Vg_UserMsg, "Observed %d times:", ec->count ); + if (ec->tid > 0) + VG_(message)(Vg_UserMsg, "Thread %d:", ec->tid ); switch (ec->ekind) { case ValueErr: if (ec->size == 0) { @@ -441,6 +449,8 @@ static void VG_(maybe_add_context) ( ErrContext* ec ) static Bool slowdown_message = False; static Int vg_n_errs_shown = 0; + vg_assert(ec->tid >= 0 && ec->tid < VG_N_THREADS); + /* After M_VG_COLLECT_NO_ERRORS_AFTER different errors have been found, just refuse to collect any more. */ if (vg_n_errs_shown >= M_VG_COLLECT_NO_ERRORS_AFTER) { @@ -551,6 +561,7 @@ void VG_(record_value_error) ( Int size ) VG_(baseBlock)[VGOFF_(m_ebp)] ); ec.ekind = ValueErr; ec.size = size; + ec.tid = VG_(get_current_tid)(); VG_(maybe_add_context) ( &ec ); } @@ -573,26 +584,27 @@ void VG_(record_address_error) ( Addr a, Int size, Bool isWrite ) ec.axskind = isWrite ? WriteAxs : ReadAxs; ec.size = size; ec.addr = a; + ec.tid = VG_(get_current_tid)(); VG_(describe_addr) ( a, &ec.addrinfo ); VG_(maybe_add_context) ( &ec ); } -void VG_(record_jump_error) ( Addr a ) +void VG_(record_free_error) ( Addr a ) { ErrContext ec; clear_ErrContext( &ec ); ec.count = 1; ec.next = NULL; - ec.where = VG_(get_ExeContext)( False, VG_(baseBlock)[VGOFF_(m_eip)], - VG_(baseBlock)[VGOFF_(m_ebp)] ); - ec.ekind = AddrErr; - ec.axskind = ExecAxs; + ec.where = VG_(get_ExeContext)( True, VG_(baseBlock)[VGOFF_(m_eip)], + VG_(baseBlock)[VGOFF_(m_ebp)] ); + ec.ekind = FreeErr; ec.addr = a; + ec.tid = VG_(get_current_tid)(); VG_(describe_addr) ( a, &ec.addrinfo ); VG_(maybe_add_context) ( &ec ); } -void VG_(record_free_error) ( Addr a ) +void VG_(record_freemismatch_error) ( Addr a ) { ErrContext ec; clear_ErrContext( &ec ); @@ -600,30 +612,33 @@ void VG_(record_free_error) ( Addr a ) ec.next = NULL; ec.where = VG_(get_ExeContext)( True, VG_(baseBlock)[VGOFF_(m_eip)], VG_(baseBlock)[VGOFF_(m_ebp)] ); - ec.ekind = FreeErr; + ec.ekind = FreeMismatchErr; ec.addr = a; + ec.tid = VG_(get_current_tid)(); VG_(describe_addr) ( a, &ec.addrinfo ); VG_(maybe_add_context) ( &ec ); } -void VG_(record_freemismatch_error) ( Addr a ) + +/* These three are called not from generated code but in response to + requests passed back to the scheduler. So we pick up %EIP/%EBP + values from the stored thread state, not from VG_(baseBlock). */ + +void VG_(record_jump_error) ( ThreadState* tst, Addr a ) { ErrContext ec; clear_ErrContext( &ec ); ec.count = 1; ec.next = NULL; - ec.where = VG_(get_ExeContext)( True, VG_(baseBlock)[VGOFF_(m_eip)], - VG_(baseBlock)[VGOFF_(m_ebp)] ); - ec.ekind = FreeMismatchErr; + ec.where = VG_(get_ExeContext)( False, tst->m_eip, tst->m_ebp ); + ec.ekind = AddrErr; + ec.axskind = ExecAxs; ec.addr = a; + ec.tid = tst->tid; VG_(describe_addr) ( a, &ec.addrinfo ); VG_(maybe_add_context) ( &ec ); } -/* These two are called not from generated code but in response to - requests passed back to the scheduler. So we pick up %EIP/%EBP - values from the stored thread state, not from VG_(baseBlock). */ - void VG_(record_param_err) ( ThreadState* tst, Addr a, Bool isWriteLack, Char* msg ) { @@ -634,13 +649,13 @@ void VG_(record_param_err) ( ThreadState* tst, Addr a, Bool isWriteLack, ec.where = VG_(get_ExeContext)( False, tst->m_eip, tst->m_ebp ); ec.ekind = ParamErr; ec.addr = a; + ec.tid = tst->tid; VG_(describe_addr) ( a, &ec.addrinfo ); ec.syscall_param = msg; ec.isWriteableLack = isWriteLack; VG_(maybe_add_context) ( &ec ); } - void VG_(record_user_err) ( ThreadState* tst, Addr a, Bool isWriteLack ) { ErrContext ec; @@ -650,6 +665,7 @@ void VG_(record_user_err) ( ThreadState* tst, Addr a, Bool isWriteLack ) ec.where = VG_(get_ExeContext)( False, tst->m_eip, tst->m_ebp ); ec.ekind = UserErr; ec.addr = a; + ec.tid = tst->tid; VG_(describe_addr) ( a, &ec.addrinfo ); ec.isWriteableLack = isWriteLack; VG_(maybe_add_context) ( &ec ); @@ -710,7 +726,8 @@ void VG_(show_all_errors) ( void ) pp_ErrContext( p_min, False ); if ((i+1 == VG_(clo_dump_error))) { - VG_(translate) ( p_min->where->eips[0], NULL, NULL, NULL ); + VG_(translate) ( 0 /* dummy ThreadId; irrelevant due to below NULLs */, + p_min->where->eips[0], NULL, NULL, NULL ); } p_min->count = 1 << 30; diff --git a/coregrind/vg_include.h b/coregrind/vg_include.h index c4ba60310b..c9be884271 100644 --- a/coregrind/vg_include.h +++ b/coregrind/vg_include.h @@ -135,7 +135,7 @@ #define VG_N_WAITING_FDS 10 /* Maximum number of mutexes allowed. */ -#define VG_N_MUTEXES 10 +#define VG_N_MUTEXES 30 /* --------------------------------------------------------------------- @@ -461,7 +461,11 @@ typedef struct { /* The thread identity is simply the index in vg_threads[]. ThreadId == 0 is the root thread and has the special property - that we don't try and allocate or deallocate its stack. */ + that we don't try and allocate or deallocate its stack. For + convenience of generating error message, we also put the + ThreadId in this tid field, but be aware that it should + ALWAYS just == the index in vg_threads[]. */ + ThreadId tid; /* Current scheduling status. */ ThreadStatus status; @@ -498,6 +502,12 @@ typedef */ Addr stack_base; + /* Address of the highest legitimate word in this stack. This is + used for error messages only -- not critical for execution + correctness. Is is set for all stacks, specifically including + ThreadId == 0 (the main thread). */ + Addr stack_highest_word; + /* Saved machine context. */ UInt m_eax; UInt m_ebx; @@ -535,10 +545,16 @@ extern void VG_(save_thread_state)( ThreadId ); /* Get the thread state block for the specified thread. */ extern ThreadState* VG_(get_thread_state)( ThreadId ); +/* And for the currently running one, if valid. */ +extern ThreadState* VG_(get_current_thread_state) ( void ); + +/* Similarly ... */ +extern ThreadId VG_(get_current_tid) ( void ); + +/* Which thread is this address in the stack of, if any? Used for + error message generation. */ +extern ThreadId VG_(identify_stack_addr)( Addr a ); -/* Create, and add to TT/TC, the translation of a client basic - block. */ -extern void VG_(create_translation_for) ( Addr orig_addr ); /* Return codes from the scheduler. */ typedef @@ -959,7 +975,8 @@ extern Int VG_(getNewShadow) ( UCodeBlock* cb ); Exports of vg_translate.c ------------------------------------------------------------------ */ -extern void VG_(translate) ( Addr orig_addr, +extern void VG_(translate) ( ThreadState* tst, + Addr orig_addr, UInt* orig_size, Addr* trans_addr, UInt* trans_size ); @@ -1054,7 +1071,8 @@ extern void VG_(record_free_error) ( Addr a ); extern void VG_(record_freemismatch_error) ( Addr a ); extern void VG_(record_address_error) ( Addr a, Int size, Bool isWrite ); -extern void VG_(record_jump_error) ( Addr a ); + +extern void VG_(record_jump_error) ( ThreadState* tst, Addr a ); extern void VG_(record_param_err) ( ThreadState* tst, Addr a, @@ -1080,6 +1098,8 @@ typedef Int rwoffset; /* Freed, Mallocd */ ExeContext* lastchange; + /* Stack */ + ThreadId stack_tid; } AddrInfo; @@ -1357,11 +1377,8 @@ extern Bool VG_(is_just_below_ESP)( Addr esp, Addr aa ); /* Nasty kludgery to deal with applications which switch stacks, like netscape. */ -#define VG_STACK_STARTS_AT 0xC0000000 #define VG_PLAUSIBLE_STACK_SIZE 8000000 -extern Bool VG_(is_plausible_stack_addr) ( Addr ); - /* --------------------------------------------------------------------- Exports of vg_syscall_mem.c diff --git a/coregrind/vg_libpthread.c b/coregrind/vg_libpthread.c index f32c5533ea..8ee3ccb10e 100644 --- a/coregrind/vg_libpthread.c +++ b/coregrind/vg_libpthread.c @@ -499,6 +499,13 @@ int send(int s, const void *msg, size_t len, int flags) return __libc_send(s, msg, len, flags); } +extern +int __libc_recv(int s, void *buf, size_t len, int flags); +int recv(int s, void *buf, size_t len, int flags) +{ + return __libc_recv(s, buf, len, flags); +} + /*--------------------------------------------------*/ diff --git a/coregrind/vg_memory.c b/coregrind/vg_memory.c index 48c11f3e23..9d3cf86e7c 100644 --- a/coregrind/vg_memory.c +++ b/coregrind/vg_memory.c @@ -1314,14 +1314,15 @@ void fpu_write_check_SLOWLY ( Addr addr, Int size ) addresses below %esp are not live; those at and above it are. */ -/* Does this address look like something in the program's main - stack ? */ -Bool VG_(is_plausible_stack_addr) ( Addr aa ) +/* Does this address look like something in or vaguely near the + current thread's stack? */ +static +Bool is_plausible_stack_addr ( ThreadState* tst, Addr aa ) { UInt a = (UInt)aa; PROF_EVENT(100); - if (a < VG_STACK_STARTS_AT && - a > VG_STACK_STARTS_AT - VG_PLAUSIBLE_STACK_SIZE) + if (a <= tst->stack_highest_word && + a > tst->stack_highest_word - VG_PLAUSIBLE_STACK_SIZE) return True; else return False; @@ -1466,6 +1467,7 @@ static void vg_handle_esp_assignment_SLOWLY ( Addr new_espA ) - 0 * VKI_BYTES_PER_PAGE; Addr valid_up_to = get_page_base(new_esp) + VKI_BYTES_PER_PAGE + 0 * VKI_BYTES_PER_PAGE; + ThreadState* tst = VG_(get_current_thread_state)(); PROF_EVENT(114); if (VG_(clo_verbosity) > 1) VG_(message)(Vg_UserMsg, "Warning: client switching stacks? " @@ -1474,7 +1476,7 @@ static void vg_handle_esp_assignment_SLOWLY ( Addr new_espA ) /* VG_(printf)("na %p, %%esp %p, wr %p\n", invalid_down_to, new_esp, valid_up_to ); */ VGM_(make_noaccess) ( invalid_down_to, new_esp - invalid_down_to ); - if (!VG_(is_plausible_stack_addr)(new_esp)) { + if (!is_plausible_stack_addr(tst, new_esp)) { VGM_(make_readable) ( new_esp, valid_up_to - new_esp ); } } diff --git a/coregrind/vg_scheduler.c b/coregrind/vg_scheduler.c index fadde58fc4..fb0852e82f 100644 --- a/coregrind/vg_scheduler.c +++ b/coregrind/vg_scheduler.c @@ -73,6 +73,9 @@ suitable for use by anyone at all! /* Private globals. A statically allocated array of threads. */ static ThreadState vg_threads[VG_N_THREADS]; +/* The tid of the thread currently in VG_(baseBlock). */ +static Int vg_tid_currently_in_baseBlock = VG_INVALID_THREADID; + /* vg_oursignalhandler() might longjmp(). Here's the jmp_buf. */ jmp_buf VG_(scheduler_jmpbuf); @@ -131,6 +134,39 @@ static void do_nontrivial_clientreq ( ThreadId tid ); Helper functions for the scheduler. ------------------------------------------------------------------ */ +/* For constructing error messages only: try and identify a thread + whose stack this address currently falls within, or return + VG_INVALID_THREADID if it doesn't. A small complication is dealing + with any currently VG_(baseBlock)-resident thread. +*/ +ThreadId VG_(identify_stack_addr)( Addr a ) +{ + ThreadId tid, tid_to_skip; + + tid_to_skip = VG_INVALID_THREADID; + + /* First check to see if there's a currently-loaded thread in + VG_(baseBlock). */ + if (vg_tid_currently_in_baseBlock != VG_INVALID_THREADID) { + tid = vg_tid_currently_in_baseBlock; + if (VG_(baseBlock)[VGOFF_(m_esp)] <= a + && a <= vg_threads[tid].stack_highest_word) + return tid; + else + tid_to_skip = tid; + } + + for (tid = 0; tid < VG_N_THREADS; tid++) { + if (vg_threads[tid].status == VgTs_Empty) continue; + if (tid == tid_to_skip) continue; + if (vg_threads[tid].m_esp <= a + && a <= vg_threads[tid].stack_highest_word) + return tid; + } + return VG_INVALID_THREADID; +} + + /* Print the scheduler status. */ void VG_(pp_sched_status) ( void ) { @@ -212,14 +248,16 @@ Char* name_of_sched_event ( UInt event ) orig_addr, and add it to the translation cache & translation table. This probably doesn't really belong here, but, hey ... */ -void VG_(create_translation_for) ( Addr orig_addr ) +static +void create_translation_for ( ThreadId tid, Addr orig_addr ) { Addr trans_addr; TTEntry tte; Int orig_size, trans_size; /* Ensure there is space to hold a translation. */ VG_(maybe_do_lru_pass)(); - VG_(translate)( orig_addr, &orig_size, &trans_addr, &trans_size ); + VG_(translate)( &vg_threads[tid], + orig_addr, &orig_size, &trans_addr, &trans_size ); /* Copy data at trans_addr into the translation cache. Returned pointer is to the code, not to the 4-byte header. */ @@ -273,6 +311,20 @@ ThreadState* VG_(get_thread_state) ( ThreadId tid ) } +ThreadState* VG_(get_current_thread_state) ( void ) +{ + vg_assert(vg_tid_currently_in_baseBlock != VG_INVALID_THREADID); + return VG_(get_thread_state) ( VG_INVALID_THREADID ); +} + + +ThreadId VG_(get_current_tid) ( void ) +{ + vg_assert(vg_tid_currently_in_baseBlock != VG_INVALID_THREADID); + return vg_tid_currently_in_baseBlock; +} + + /* Find an unused VgMutex record. */ static MutexId vg_alloc_VgMutex ( void ) @@ -295,6 +347,8 @@ __inline__ void VG_(load_thread_state) ( ThreadId tid ) { Int i; + vg_assert(vg_tid_currently_in_baseBlock == VG_INVALID_THREADID); + VG_(baseBlock)[VGOFF_(m_eax)] = vg_threads[tid].m_eax; VG_(baseBlock)[VGOFF_(m_ebx)] = vg_threads[tid].m_ebx; VG_(baseBlock)[VGOFF_(m_ecx)] = vg_threads[tid].m_ecx; @@ -318,6 +372,8 @@ void VG_(load_thread_state) ( ThreadId tid ) VG_(baseBlock)[VGOFF_(sh_ebp)] = vg_threads[tid].sh_ebp; VG_(baseBlock)[VGOFF_(sh_esp)] = vg_threads[tid].sh_esp; VG_(baseBlock)[VGOFF_(sh_eflags)] = vg_threads[tid].sh_eflags; + + vg_tid_currently_in_baseBlock = tid; } @@ -333,6 +389,8 @@ void VG_(save_thread_state) ( ThreadId tid ) Int i; const UInt junk = 0xDEADBEEF; + vg_assert(vg_tid_currently_in_baseBlock != VG_INVALID_THREADID); + vg_threads[tid].m_eax = VG_(baseBlock)[VGOFF_(m_eax)]; vg_threads[tid].m_ebx = VG_(baseBlock)[VGOFF_(m_ebx)]; vg_threads[tid].m_ecx = VG_(baseBlock)[VGOFF_(m_ecx)]; @@ -371,6 +429,8 @@ void VG_(save_thread_state) ( ThreadId tid ) for (i = 0; i < VG_SIZE_OF_FPUSTATE_W; i++) VG_(baseBlock)[VGOFF_(m_fpustate) + i] = junk; + + vg_tid_currently_in_baseBlock = VG_INVALID_THREADID; } @@ -450,6 +510,7 @@ void VG_(scheduler_init) ( void ) for (i = 0; i < VG_N_THREADS; i++) { vg_threads[i].stack_size = 0; vg_threads[i].stack_base = (Addr)NULL; + vg_threads[i].tid = i; } for (i = 0; i < VG_N_WAITING_FDS; i++) @@ -466,9 +527,15 @@ void VG_(scheduler_init) ( void ) vg_threads[tid_main].status = VgTs_Runnable; vg_threads[tid_main].joiner = VG_INVALID_THREADID; vg_threads[tid_main].retval = NULL; /* not important */ + vg_threads[tid_main].stack_highest_word + = vg_threads[tid_main].m_esp /* -4 ??? */; /* Copy VG_(baseBlock) state to tid_main's slot. */ + vg_tid_currently_in_baseBlock = tid_main; VG_(save_thread_state) ( tid_main ); + + /* So now ... */ + vg_assert(vg_tid_currently_in_baseBlock == VG_INVALID_THREADID); } @@ -1037,6 +1104,9 @@ VgSchedReturnCode VG_(scheduler) ( void ) /* ... and remember what we asked for. */ dispatch_ctr_SAVED = VG_(dispatch_ctr); + /* paranoia ... */ + vg_assert(vg_threads[tid].tid == tid); + /* Actually run thread tid. */ while (True) { @@ -1061,7 +1131,7 @@ VgSchedReturnCode VG_(scheduler) ( void ) = VG_(search_transtab) ( vg_threads[tid].m_eip ); if (trans_addr == (Addr)0) { /* Not found; we need to request a translation. */ - VG_(create_translation_for)( vg_threads[tid].m_eip ); + create_translation_for( tid, vg_threads[tid].m_eip ); trans_addr = VG_(search_transtab) ( vg_threads[tid].m_eip ); if (trans_addr == (Addr)0) VG_(panic)("VG_TRC_INNER_FASTMISS: missing tt_fast entry"); @@ -1191,7 +1261,7 @@ VgSchedReturnCode VG_(scheduler) ( void ) throwing away the result. */ VG_(printf)( "======vvvvvvvv====== LAST TRANSLATION ======vvvvvvvv======\n"); - VG_(translate)( vg_threads[tid].m_eip, NULL, NULL, NULL ); + VG_(translate)( &vg_threads[tid], vg_threads[tid].m_eip, NULL, NULL, NULL ); VG_(printf)("\n"); VG_(printf)( "======^^^^^^^^====== LAST TRANSLATION ======^^^^^^^^======\n"); @@ -1388,7 +1458,7 @@ void do_pthread_create ( ThreadId parent_tid, vg_assert(vg_threads[parent_tid].status != VgTs_Empty); - tid = vg_alloc_ThreadState(); + tid = vg_alloc_ThreadState(); /* If we've created the main thread's tid, we're in deep trouble :) */ vg_assert(tid != 0); @@ -1413,10 +1483,16 @@ void do_pthread_create ( ThreadId parent_tid, new_stack = (Addr)VG_(get_memory_from_mmap)( new_stk_szb ); vg_threads[tid].stack_base = new_stack; vg_threads[tid].stack_size = new_stk_szb; - vg_threads[tid].m_esp + vg_threads[tid].stack_highest_word = new_stack + new_stk_szb - - VG_AR_CLIENT_STACKBASE_REDZONE_SZB; + - VG_AR_CLIENT_STACKBASE_REDZONE_SZB; /* -4 ??? */; } + + vg_threads[tid].m_esp + = vg_threads[tid].stack_base + + vg_threads[tid].stack_size + - VG_AR_CLIENT_STACKBASE_REDZONE_SZB; + if (VG_(clo_instrument)) VGM_(make_noaccess)( vg_threads[tid].m_esp, VG_AR_CLIENT_STACKBASE_REDZONE_SZB ); diff --git a/coregrind/vg_translate.c b/coregrind/vg_translate.c index 19bdfb5d8a..73cad5e9bc 100644 --- a/coregrind/vg_translate.c +++ b/coregrind/vg_translate.c @@ -3039,7 +3039,9 @@ static void vg_cleanup ( UCodeBlock* cb ) throw away the translation once it is made, and (b) produce a load of debugging output. */ -void VG_(translate) ( Addr orig_addr, +void VG_(translate) ( ThreadState* tst, + /* Identity of thread needing this block */ + Addr orig_addr, UInt* orig_size, Addr* trans_addr, UInt* trans_size ) @@ -3063,7 +3065,7 @@ void VG_(translate) ( Addr orig_addr, Addr bad_addr; Bool ok = VGM_(check_readable) ( orig_addr, 1, &bad_addr ); if (!ok) { - VG_(record_jump_error)(bad_addr); + VG_(record_jump_error)(tst, bad_addr); } } diff --git a/vg_clientmalloc.c b/vg_clientmalloc.c index dca0e3e5cb..b1848405aa 100644 --- a/vg_clientmalloc.c +++ b/vg_clientmalloc.c @@ -529,15 +529,17 @@ void VG_(describe_addr) ( Addr a, AddrInfo* ai ) ShadowChunk* sc; UInt ml_no; Bool ok; + ThreadId tid; /* Perhaps it's a user-def'd block ? */ ok = VG_(client_perm_maybe_describe)( a, ai ); if (ok) return; - /* Perhaps it's on the stack? */ - if (VG_(is_plausible_stack_addr)(a) - && a >= (Addr)VG_(baseBlock)[VGOFF_(m_esp)]) { - ai->akind = Stack; + /* Perhaps it's on a thread's stack? */ + tid = VG_(identify_stack_addr)(a); + if (tid != VG_INVALID_THREADID) { + ai->akind = Stack; + ai->stack_tid = tid; return; } /* Search for a freed block which might bracket it. */ diff --git a/vg_errcontext.c b/vg_errcontext.c index c632124eaa..0ac2cb36b4 100644 --- a/vg_errcontext.c +++ b/vg_errcontext.c @@ -142,6 +142,8 @@ typedef Char* syscall_param; /* Param, User */ Bool isWriteableLack; + /* ALL */ + ThreadId tid; } ErrContext; @@ -174,6 +176,7 @@ static void clear_AddrInfo ( AddrInfo* ai ) ai->blksize = 0; ai->rwoffset = 0; ai->lastchange = NULL; + ai->stack_tid = VG_INVALID_THREADID; } static void clear_ErrContext ( ErrContext* ec ) @@ -189,6 +192,7 @@ static void clear_ErrContext ( ErrContext* ec ) clear_AddrInfo ( &ec->addrinfo ); ec->syscall_param = NULL; ec->isWriteableLack = False; + ec->tid = VG_INVALID_THREADID; } @@ -264,7 +268,9 @@ static void pp_AddrInfo ( Addr a, AddrInfo* ai ) { switch (ai->akind) { case Stack: - VG_(message)(Vg_UserMsg, " Address 0x%x is on the stack", a); + VG_(message)(Vg_UserMsg, + " Address 0x%x is on thread %d's stack", + ai->stack_tid, a); break; case Unknown: VG_(message)(Vg_UserMsg, @@ -309,6 +315,8 @@ static void pp_ErrContext ( ErrContext* ec, Bool printCount ) { if (printCount) VG_(message)(Vg_UserMsg, "Observed %d times:", ec->count ); + if (ec->tid > 0) + VG_(message)(Vg_UserMsg, "Thread %d:", ec->tid ); switch (ec->ekind) { case ValueErr: if (ec->size == 0) { @@ -441,6 +449,8 @@ static void VG_(maybe_add_context) ( ErrContext* ec ) static Bool slowdown_message = False; static Int vg_n_errs_shown = 0; + vg_assert(ec->tid >= 0 && ec->tid < VG_N_THREADS); + /* After M_VG_COLLECT_NO_ERRORS_AFTER different errors have been found, just refuse to collect any more. */ if (vg_n_errs_shown >= M_VG_COLLECT_NO_ERRORS_AFTER) { @@ -551,6 +561,7 @@ void VG_(record_value_error) ( Int size ) VG_(baseBlock)[VGOFF_(m_ebp)] ); ec.ekind = ValueErr; ec.size = size; + ec.tid = VG_(get_current_tid)(); VG_(maybe_add_context) ( &ec ); } @@ -573,26 +584,27 @@ void VG_(record_address_error) ( Addr a, Int size, Bool isWrite ) ec.axskind = isWrite ? WriteAxs : ReadAxs; ec.size = size; ec.addr = a; + ec.tid = VG_(get_current_tid)(); VG_(describe_addr) ( a, &ec.addrinfo ); VG_(maybe_add_context) ( &ec ); } -void VG_(record_jump_error) ( Addr a ) +void VG_(record_free_error) ( Addr a ) { ErrContext ec; clear_ErrContext( &ec ); ec.count = 1; ec.next = NULL; - ec.where = VG_(get_ExeContext)( False, VG_(baseBlock)[VGOFF_(m_eip)], - VG_(baseBlock)[VGOFF_(m_ebp)] ); - ec.ekind = AddrErr; - ec.axskind = ExecAxs; + ec.where = VG_(get_ExeContext)( True, VG_(baseBlock)[VGOFF_(m_eip)], + VG_(baseBlock)[VGOFF_(m_ebp)] ); + ec.ekind = FreeErr; ec.addr = a; + ec.tid = VG_(get_current_tid)(); VG_(describe_addr) ( a, &ec.addrinfo ); VG_(maybe_add_context) ( &ec ); } -void VG_(record_free_error) ( Addr a ) +void VG_(record_freemismatch_error) ( Addr a ) { ErrContext ec; clear_ErrContext( &ec ); @@ -600,30 +612,33 @@ void VG_(record_free_error) ( Addr a ) ec.next = NULL; ec.where = VG_(get_ExeContext)( True, VG_(baseBlock)[VGOFF_(m_eip)], VG_(baseBlock)[VGOFF_(m_ebp)] ); - ec.ekind = FreeErr; + ec.ekind = FreeMismatchErr; ec.addr = a; + ec.tid = VG_(get_current_tid)(); VG_(describe_addr) ( a, &ec.addrinfo ); VG_(maybe_add_context) ( &ec ); } -void VG_(record_freemismatch_error) ( Addr a ) + +/* These three are called not from generated code but in response to + requests passed back to the scheduler. So we pick up %EIP/%EBP + values from the stored thread state, not from VG_(baseBlock). */ + +void VG_(record_jump_error) ( ThreadState* tst, Addr a ) { ErrContext ec; clear_ErrContext( &ec ); ec.count = 1; ec.next = NULL; - ec.where = VG_(get_ExeContext)( True, VG_(baseBlock)[VGOFF_(m_eip)], - VG_(baseBlock)[VGOFF_(m_ebp)] ); - ec.ekind = FreeMismatchErr; + ec.where = VG_(get_ExeContext)( False, tst->m_eip, tst->m_ebp ); + ec.ekind = AddrErr; + ec.axskind = ExecAxs; ec.addr = a; + ec.tid = tst->tid; VG_(describe_addr) ( a, &ec.addrinfo ); VG_(maybe_add_context) ( &ec ); } -/* These two are called not from generated code but in response to - requests passed back to the scheduler. So we pick up %EIP/%EBP - values from the stored thread state, not from VG_(baseBlock). */ - void VG_(record_param_err) ( ThreadState* tst, Addr a, Bool isWriteLack, Char* msg ) { @@ -634,13 +649,13 @@ void VG_(record_param_err) ( ThreadState* tst, Addr a, Bool isWriteLack, ec.where = VG_(get_ExeContext)( False, tst->m_eip, tst->m_ebp ); ec.ekind = ParamErr; ec.addr = a; + ec.tid = tst->tid; VG_(describe_addr) ( a, &ec.addrinfo ); ec.syscall_param = msg; ec.isWriteableLack = isWriteLack; VG_(maybe_add_context) ( &ec ); } - void VG_(record_user_err) ( ThreadState* tst, Addr a, Bool isWriteLack ) { ErrContext ec; @@ -650,6 +665,7 @@ void VG_(record_user_err) ( ThreadState* tst, Addr a, Bool isWriteLack ) ec.where = VG_(get_ExeContext)( False, tst->m_eip, tst->m_ebp ); ec.ekind = UserErr; ec.addr = a; + ec.tid = tst->tid; VG_(describe_addr) ( a, &ec.addrinfo ); ec.isWriteableLack = isWriteLack; VG_(maybe_add_context) ( &ec ); @@ -710,7 +726,8 @@ void VG_(show_all_errors) ( void ) pp_ErrContext( p_min, False ); if ((i+1 == VG_(clo_dump_error))) { - VG_(translate) ( p_min->where->eips[0], NULL, NULL, NULL ); + VG_(translate) ( 0 /* dummy ThreadId; irrelevant due to below NULLs */, + p_min->where->eips[0], NULL, NULL, NULL ); } p_min->count = 1 << 30; diff --git a/vg_include.h b/vg_include.h index c4ba60310b..c9be884271 100644 --- a/vg_include.h +++ b/vg_include.h @@ -135,7 +135,7 @@ #define VG_N_WAITING_FDS 10 /* Maximum number of mutexes allowed. */ -#define VG_N_MUTEXES 10 +#define VG_N_MUTEXES 30 /* --------------------------------------------------------------------- @@ -461,7 +461,11 @@ typedef struct { /* The thread identity is simply the index in vg_threads[]. ThreadId == 0 is the root thread and has the special property - that we don't try and allocate or deallocate its stack. */ + that we don't try and allocate or deallocate its stack. For + convenience of generating error message, we also put the + ThreadId in this tid field, but be aware that it should + ALWAYS just == the index in vg_threads[]. */ + ThreadId tid; /* Current scheduling status. */ ThreadStatus status; @@ -498,6 +502,12 @@ typedef */ Addr stack_base; + /* Address of the highest legitimate word in this stack. This is + used for error messages only -- not critical for execution + correctness. Is is set for all stacks, specifically including + ThreadId == 0 (the main thread). */ + Addr stack_highest_word; + /* Saved machine context. */ UInt m_eax; UInt m_ebx; @@ -535,10 +545,16 @@ extern void VG_(save_thread_state)( ThreadId ); /* Get the thread state block for the specified thread. */ extern ThreadState* VG_(get_thread_state)( ThreadId ); +/* And for the currently running one, if valid. */ +extern ThreadState* VG_(get_current_thread_state) ( void ); + +/* Similarly ... */ +extern ThreadId VG_(get_current_tid) ( void ); + +/* Which thread is this address in the stack of, if any? Used for + error message generation. */ +extern ThreadId VG_(identify_stack_addr)( Addr a ); -/* Create, and add to TT/TC, the translation of a client basic - block. */ -extern void VG_(create_translation_for) ( Addr orig_addr ); /* Return codes from the scheduler. */ typedef @@ -959,7 +975,8 @@ extern Int VG_(getNewShadow) ( UCodeBlock* cb ); Exports of vg_translate.c ------------------------------------------------------------------ */ -extern void VG_(translate) ( Addr orig_addr, +extern void VG_(translate) ( ThreadState* tst, + Addr orig_addr, UInt* orig_size, Addr* trans_addr, UInt* trans_size ); @@ -1054,7 +1071,8 @@ extern void VG_(record_free_error) ( Addr a ); extern void VG_(record_freemismatch_error) ( Addr a ); extern void VG_(record_address_error) ( Addr a, Int size, Bool isWrite ); -extern void VG_(record_jump_error) ( Addr a ); + +extern void VG_(record_jump_error) ( ThreadState* tst, Addr a ); extern void VG_(record_param_err) ( ThreadState* tst, Addr a, @@ -1080,6 +1098,8 @@ typedef Int rwoffset; /* Freed, Mallocd */ ExeContext* lastchange; + /* Stack */ + ThreadId stack_tid; } AddrInfo; @@ -1357,11 +1377,8 @@ extern Bool VG_(is_just_below_ESP)( Addr esp, Addr aa ); /* Nasty kludgery to deal with applications which switch stacks, like netscape. */ -#define VG_STACK_STARTS_AT 0xC0000000 #define VG_PLAUSIBLE_STACK_SIZE 8000000 -extern Bool VG_(is_plausible_stack_addr) ( Addr ); - /* --------------------------------------------------------------------- Exports of vg_syscall_mem.c diff --git a/vg_libpthread.c b/vg_libpthread.c index f32c5533ea..8ee3ccb10e 100644 --- a/vg_libpthread.c +++ b/vg_libpthread.c @@ -499,6 +499,13 @@ int send(int s, const void *msg, size_t len, int flags) return __libc_send(s, msg, len, flags); } +extern +int __libc_recv(int s, void *buf, size_t len, int flags); +int recv(int s, void *buf, size_t len, int flags) +{ + return __libc_recv(s, buf, len, flags); +} + /*--------------------------------------------------*/ diff --git a/vg_memory.c b/vg_memory.c index 48c11f3e23..9d3cf86e7c 100644 --- a/vg_memory.c +++ b/vg_memory.c @@ -1314,14 +1314,15 @@ void fpu_write_check_SLOWLY ( Addr addr, Int size ) addresses below %esp are not live; those at and above it are. */ -/* Does this address look like something in the program's main - stack ? */ -Bool VG_(is_plausible_stack_addr) ( Addr aa ) +/* Does this address look like something in or vaguely near the + current thread's stack? */ +static +Bool is_plausible_stack_addr ( ThreadState* tst, Addr aa ) { UInt a = (UInt)aa; PROF_EVENT(100); - if (a < VG_STACK_STARTS_AT && - a > VG_STACK_STARTS_AT - VG_PLAUSIBLE_STACK_SIZE) + if (a <= tst->stack_highest_word && + a > tst->stack_highest_word - VG_PLAUSIBLE_STACK_SIZE) return True; else return False; @@ -1466,6 +1467,7 @@ static void vg_handle_esp_assignment_SLOWLY ( Addr new_espA ) - 0 * VKI_BYTES_PER_PAGE; Addr valid_up_to = get_page_base(new_esp) + VKI_BYTES_PER_PAGE + 0 * VKI_BYTES_PER_PAGE; + ThreadState* tst = VG_(get_current_thread_state)(); PROF_EVENT(114); if (VG_(clo_verbosity) > 1) VG_(message)(Vg_UserMsg, "Warning: client switching stacks? " @@ -1474,7 +1476,7 @@ static void vg_handle_esp_assignment_SLOWLY ( Addr new_espA ) /* VG_(printf)("na %p, %%esp %p, wr %p\n", invalid_down_to, new_esp, valid_up_to ); */ VGM_(make_noaccess) ( invalid_down_to, new_esp - invalid_down_to ); - if (!VG_(is_plausible_stack_addr)(new_esp)) { + if (!is_plausible_stack_addr(tst, new_esp)) { VGM_(make_readable) ( new_esp, valid_up_to - new_esp ); } } diff --git a/vg_scheduler.c b/vg_scheduler.c index fadde58fc4..fb0852e82f 100644 --- a/vg_scheduler.c +++ b/vg_scheduler.c @@ -73,6 +73,9 @@ suitable for use by anyone at all! /* Private globals. A statically allocated array of threads. */ static ThreadState vg_threads[VG_N_THREADS]; +/* The tid of the thread currently in VG_(baseBlock). */ +static Int vg_tid_currently_in_baseBlock = VG_INVALID_THREADID; + /* vg_oursignalhandler() might longjmp(). Here's the jmp_buf. */ jmp_buf VG_(scheduler_jmpbuf); @@ -131,6 +134,39 @@ static void do_nontrivial_clientreq ( ThreadId tid ); Helper functions for the scheduler. ------------------------------------------------------------------ */ +/* For constructing error messages only: try and identify a thread + whose stack this address currently falls within, or return + VG_INVALID_THREADID if it doesn't. A small complication is dealing + with any currently VG_(baseBlock)-resident thread. +*/ +ThreadId VG_(identify_stack_addr)( Addr a ) +{ + ThreadId tid, tid_to_skip; + + tid_to_skip = VG_INVALID_THREADID; + + /* First check to see if there's a currently-loaded thread in + VG_(baseBlock). */ + if (vg_tid_currently_in_baseBlock != VG_INVALID_THREADID) { + tid = vg_tid_currently_in_baseBlock; + if (VG_(baseBlock)[VGOFF_(m_esp)] <= a + && a <= vg_threads[tid].stack_highest_word) + return tid; + else + tid_to_skip = tid; + } + + for (tid = 0; tid < VG_N_THREADS; tid++) { + if (vg_threads[tid].status == VgTs_Empty) continue; + if (tid == tid_to_skip) continue; + if (vg_threads[tid].m_esp <= a + && a <= vg_threads[tid].stack_highest_word) + return tid; + } + return VG_INVALID_THREADID; +} + + /* Print the scheduler status. */ void VG_(pp_sched_status) ( void ) { @@ -212,14 +248,16 @@ Char* name_of_sched_event ( UInt event ) orig_addr, and add it to the translation cache & translation table. This probably doesn't really belong here, but, hey ... */ -void VG_(create_translation_for) ( Addr orig_addr ) +static +void create_translation_for ( ThreadId tid, Addr orig_addr ) { Addr trans_addr; TTEntry tte; Int orig_size, trans_size; /* Ensure there is space to hold a translation. */ VG_(maybe_do_lru_pass)(); - VG_(translate)( orig_addr, &orig_size, &trans_addr, &trans_size ); + VG_(translate)( &vg_threads[tid], + orig_addr, &orig_size, &trans_addr, &trans_size ); /* Copy data at trans_addr into the translation cache. Returned pointer is to the code, not to the 4-byte header. */ @@ -273,6 +311,20 @@ ThreadState* VG_(get_thread_state) ( ThreadId tid ) } +ThreadState* VG_(get_current_thread_state) ( void ) +{ + vg_assert(vg_tid_currently_in_baseBlock != VG_INVALID_THREADID); + return VG_(get_thread_state) ( VG_INVALID_THREADID ); +} + + +ThreadId VG_(get_current_tid) ( void ) +{ + vg_assert(vg_tid_currently_in_baseBlock != VG_INVALID_THREADID); + return vg_tid_currently_in_baseBlock; +} + + /* Find an unused VgMutex record. */ static MutexId vg_alloc_VgMutex ( void ) @@ -295,6 +347,8 @@ __inline__ void VG_(load_thread_state) ( ThreadId tid ) { Int i; + vg_assert(vg_tid_currently_in_baseBlock == VG_INVALID_THREADID); + VG_(baseBlock)[VGOFF_(m_eax)] = vg_threads[tid].m_eax; VG_(baseBlock)[VGOFF_(m_ebx)] = vg_threads[tid].m_ebx; VG_(baseBlock)[VGOFF_(m_ecx)] = vg_threads[tid].m_ecx; @@ -318,6 +372,8 @@ void VG_(load_thread_state) ( ThreadId tid ) VG_(baseBlock)[VGOFF_(sh_ebp)] = vg_threads[tid].sh_ebp; VG_(baseBlock)[VGOFF_(sh_esp)] = vg_threads[tid].sh_esp; VG_(baseBlock)[VGOFF_(sh_eflags)] = vg_threads[tid].sh_eflags; + + vg_tid_currently_in_baseBlock = tid; } @@ -333,6 +389,8 @@ void VG_(save_thread_state) ( ThreadId tid ) Int i; const UInt junk = 0xDEADBEEF; + vg_assert(vg_tid_currently_in_baseBlock != VG_INVALID_THREADID); + vg_threads[tid].m_eax = VG_(baseBlock)[VGOFF_(m_eax)]; vg_threads[tid].m_ebx = VG_(baseBlock)[VGOFF_(m_ebx)]; vg_threads[tid].m_ecx = VG_(baseBlock)[VGOFF_(m_ecx)]; @@ -371,6 +429,8 @@ void VG_(save_thread_state) ( ThreadId tid ) for (i = 0; i < VG_SIZE_OF_FPUSTATE_W; i++) VG_(baseBlock)[VGOFF_(m_fpustate) + i] = junk; + + vg_tid_currently_in_baseBlock = VG_INVALID_THREADID; } @@ -450,6 +510,7 @@ void VG_(scheduler_init) ( void ) for (i = 0; i < VG_N_THREADS; i++) { vg_threads[i].stack_size = 0; vg_threads[i].stack_base = (Addr)NULL; + vg_threads[i].tid = i; } for (i = 0; i < VG_N_WAITING_FDS; i++) @@ -466,9 +527,15 @@ void VG_(scheduler_init) ( void ) vg_threads[tid_main].status = VgTs_Runnable; vg_threads[tid_main].joiner = VG_INVALID_THREADID; vg_threads[tid_main].retval = NULL; /* not important */ + vg_threads[tid_main].stack_highest_word + = vg_threads[tid_main].m_esp /* -4 ??? */; /* Copy VG_(baseBlock) state to tid_main's slot. */ + vg_tid_currently_in_baseBlock = tid_main; VG_(save_thread_state) ( tid_main ); + + /* So now ... */ + vg_assert(vg_tid_currently_in_baseBlock == VG_INVALID_THREADID); } @@ -1037,6 +1104,9 @@ VgSchedReturnCode VG_(scheduler) ( void ) /* ... and remember what we asked for. */ dispatch_ctr_SAVED = VG_(dispatch_ctr); + /* paranoia ... */ + vg_assert(vg_threads[tid].tid == tid); + /* Actually run thread tid. */ while (True) { @@ -1061,7 +1131,7 @@ VgSchedReturnCode VG_(scheduler) ( void ) = VG_(search_transtab) ( vg_threads[tid].m_eip ); if (trans_addr == (Addr)0) { /* Not found; we need to request a translation. */ - VG_(create_translation_for)( vg_threads[tid].m_eip ); + create_translation_for( tid, vg_threads[tid].m_eip ); trans_addr = VG_(search_transtab) ( vg_threads[tid].m_eip ); if (trans_addr == (Addr)0) VG_(panic)("VG_TRC_INNER_FASTMISS: missing tt_fast entry"); @@ -1191,7 +1261,7 @@ VgSchedReturnCode VG_(scheduler) ( void ) throwing away the result. */ VG_(printf)( "======vvvvvvvv====== LAST TRANSLATION ======vvvvvvvv======\n"); - VG_(translate)( vg_threads[tid].m_eip, NULL, NULL, NULL ); + VG_(translate)( &vg_threads[tid], vg_threads[tid].m_eip, NULL, NULL, NULL ); VG_(printf)("\n"); VG_(printf)( "======^^^^^^^^====== LAST TRANSLATION ======^^^^^^^^======\n"); @@ -1388,7 +1458,7 @@ void do_pthread_create ( ThreadId parent_tid, vg_assert(vg_threads[parent_tid].status != VgTs_Empty); - tid = vg_alloc_ThreadState(); + tid = vg_alloc_ThreadState(); /* If we've created the main thread's tid, we're in deep trouble :) */ vg_assert(tid != 0); @@ -1413,10 +1483,16 @@ void do_pthread_create ( ThreadId parent_tid, new_stack = (Addr)VG_(get_memory_from_mmap)( new_stk_szb ); vg_threads[tid].stack_base = new_stack; vg_threads[tid].stack_size = new_stk_szb; - vg_threads[tid].m_esp + vg_threads[tid].stack_highest_word = new_stack + new_stk_szb - - VG_AR_CLIENT_STACKBASE_REDZONE_SZB; + - VG_AR_CLIENT_STACKBASE_REDZONE_SZB; /* -4 ??? */; } + + vg_threads[tid].m_esp + = vg_threads[tid].stack_base + + vg_threads[tid].stack_size + - VG_AR_CLIENT_STACKBASE_REDZONE_SZB; + if (VG_(clo_instrument)) VGM_(make_noaccess)( vg_threads[tid].m_esp, VG_AR_CLIENT_STACKBASE_REDZONE_SZB ); diff --git a/vg_translate.c b/vg_translate.c index 19bdfb5d8a..73cad5e9bc 100644 --- a/vg_translate.c +++ b/vg_translate.c @@ -3039,7 +3039,9 @@ static void vg_cleanup ( UCodeBlock* cb ) throw away the translation once it is made, and (b) produce a load of debugging output. */ -void VG_(translate) ( Addr orig_addr, +void VG_(translate) ( ThreadState* tst, + /* Identity of thread needing this block */ + Addr orig_addr, UInt* orig_size, Addr* trans_addr, UInt* trans_size ) @@ -3063,7 +3065,7 @@ void VG_(translate) ( Addr orig_addr, Addr bad_addr; Bool ok = VGM_(check_readable) ( orig_addr, 1, &bad_addr ); if (!ok) { - VG_(record_jump_error)(bad_addr); + VG_(record_jump_error)(tst, bad_addr); } }