mirror of
https://github.com/Zenithsiz/ftmemsim-valgrind.git
synced 2026-02-04 02:18:37 +00:00
Change the way thread termination is handled. Until now, there has
been a concept of a 'master thread'. This is the first thread in the process. There was special logic which kept the master thread alive artificially should it attempt to exit before its children. So the master would wait for all children to exit and then exit itself, in the process emitting the final summary of errors, leaks, etc. This has the advantage that any process waiting on this one will see the final summaries appearing before its sys_wait call returns. In other words, the final summary output is synchronous with the master-thread exiting. Unfortunately the master-thread idea has a serious drawback, namely that it can and sometimes does cause threaded programs to deadlock at exit. It introduces an artificial dependency which is that the master thread cannot really exit until all its children have exited. If -- by any means at all -- the children are waiting for the master to exit before exiting themselves, deadlock results. There are now two known examples of such deadlocks. This commit removes the master thread concept and lets threads exit in the order which they would have exited without Valgrind's involvement. The last thread to exit prints the final summaries. This has the disadvantage that final output may appear arbitrarily later relative to the exit of the initial thread. Whether this is a problem in practice remains to be seen. As a minor side effect of this change, some functions have had _NORETURN added to their names. Such functions do not return. The thread in which they execute is guaranteed to exit before they return. This makes the logic somewhat easier to follow. amd64 compilation is now broken. I will fix it shortly. git-svn-id: svn://svn.valgrind.org/valgrind/trunk@3816
This commit is contained in:
parent
fad1c6832a
commit
2479bb6b9b
@ -345,7 +345,10 @@ extern VgSchedReturnCode VG_(scheduler) ( ThreadId tid );
|
||||
|
||||
// Do everything which needs doing before the process finally ends,
|
||||
// like printing reports, etc
|
||||
extern void VG_(shutdown_actions)(ThreadId tid);
|
||||
extern void VG_(shutdown_actions_NORETURN) (
|
||||
ThreadId tid,
|
||||
VgSchedReturnCode tids_schedretcode
|
||||
);
|
||||
|
||||
extern void VG_(scheduler_init) ( void );
|
||||
|
||||
@ -524,12 +527,6 @@ extern Int VG_(clexecfd);
|
||||
Char* VG_(build_child_VALGRINDCLO) ( Char* exename );
|
||||
Char* VG_(build_child_exename) ( void );
|
||||
|
||||
/* The master thread the one which will be responsible for mopping
|
||||
everything up at exit. Normally it is tid 1, since that's the
|
||||
first thread created, but it may be something else after a
|
||||
fork(). */
|
||||
extern ThreadId VG_(master_tid);
|
||||
|
||||
/* Called when some unhandleable client behaviour is detected.
|
||||
Prints a msg and aborts. */
|
||||
extern void VG_(unimplemented) ( Char* msg )
|
||||
@ -608,21 +605,23 @@ extern void
|
||||
/*MOD*/ ThreadArchState* arch );
|
||||
|
||||
// OS/Platform-specific thread clear (after thread exit)
|
||||
extern void VGA_(os_state_clear)(ThreadState *);
|
||||
extern void VGO_(os_state_clear)(ThreadState *);
|
||||
|
||||
// OS/Platform-specific thread init (at scheduler init time)
|
||||
extern void VGA_(os_state_init)(ThreadState *);
|
||||
extern void VGO_(os_state_init)(ThreadState *);
|
||||
|
||||
// Run a thread from beginning to end. Does not return if tid == VG_(master_tid).
|
||||
void VGA_(thread_wrapper)(Word /*ThreadId*/ tid);
|
||||
// Run a thread from beginning to end.
|
||||
extern VgSchedReturnCode VGO_(thread_wrapper)(Word /*ThreadId*/ tid);
|
||||
|
||||
// Like VGA_(thread_wrapper), but it allocates a stack before calling
|
||||
// to VGA_(thread_wrapper) on that stack, as if it had been set up by
|
||||
// clone()
|
||||
void VGA_(main_thread_wrapper)(ThreadId tid) __attribute__ ((__noreturn__));
|
||||
// Call here to exit the entire Valgrind system.
|
||||
extern void VGO_(terminate_NORETURN)(ThreadId tid, VgSchedReturnCode src);
|
||||
|
||||
// Allocates a stack for the first thread, then runs it,
|
||||
// as if the thread had been set up by clone()
|
||||
extern void VGP_(main_thread_wrapper_NORETURN)(ThreadId tid);
|
||||
|
||||
// Return how many bytes of a thread's Valgrind stack are unused
|
||||
SSizeT VGA_(stack_unused)(ThreadId tid);
|
||||
extern SSizeT VGA_(stack_unused)(ThreadId tid);
|
||||
|
||||
// wait until all other threads are dead
|
||||
extern void VGA_(reap_threads)(ThreadId self);
|
||||
|
||||
@ -29,26 +29,37 @@
|
||||
*/
|
||||
|
||||
#include "core.h"
|
||||
#include "pub_core_debuglog.h"
|
||||
#include "pub_core_options.h"
|
||||
#include "pub_core_tooliface.h"
|
||||
|
||||
void VGA_(os_state_clear)(ThreadState *tst)
|
||||
void VGO_(os_state_clear)(ThreadState *tst)
|
||||
{
|
||||
tst->os_state.lwpid = 0;
|
||||
tst->os_state.threadgroup = 0;
|
||||
}
|
||||
|
||||
void VGA_(os_state_init)(ThreadState *tst)
|
||||
void VGO_(os_state_init)(ThreadState *tst)
|
||||
{
|
||||
tst->os_state.valgrind_stack_base = 0;
|
||||
tst->os_state.valgrind_stack_szB = 0;
|
||||
|
||||
VGA_(os_state_clear)(tst);
|
||||
VGO_(os_state_clear)(tst);
|
||||
}
|
||||
|
||||
static void terminate(ThreadId tid, VgSchedReturnCode src)
|
||||
static Bool i_am_the_only_thread ( void )
|
||||
{
|
||||
vg_assert(tid == VG_(master_tid));
|
||||
Int c = VG_(count_living_threads)();
|
||||
vg_assert(c >= 1); /* stay sane */
|
||||
return c == 1;
|
||||
}
|
||||
|
||||
|
||||
void VGO_(terminate_NORETURN)(ThreadId tid, VgSchedReturnCode src)
|
||||
{
|
||||
VG_(debugLog)(1, "core_os",
|
||||
"VGO_(terminate_NORETURN)(tid=%lld)\n", (ULong)tid);
|
||||
|
||||
vg_assert(VG_(count_living_threads)() == 0);
|
||||
|
||||
//--------------------------------------------------------------
|
||||
@ -56,15 +67,15 @@ static void terminate(ThreadId tid, VgSchedReturnCode src)
|
||||
//--------------------------------------------------------------
|
||||
switch (src) {
|
||||
case VgSrc_ExitSyscall: /* the normal way out */
|
||||
VG_(exit)( VG_(threads)[VG_(master_tid)].os_state.exitcode );
|
||||
VG_(exit)( VG_(threads)[tid].os_state.exitcode );
|
||||
/* NOT ALIVE HERE! */
|
||||
VG_(core_panic)("entered the afterlife in main() -- ExitSyscall");
|
||||
break; /* what the hell :) */
|
||||
|
||||
case VgSrc_FatalSig:
|
||||
/* We were killed by a fatal signal, so replicate the effect */
|
||||
vg_assert(VG_(threads)[VG_(master_tid)].os_state.fatalsig != 0);
|
||||
VG_(kill_self)(VG_(threads)[VG_(master_tid)].os_state.fatalsig);
|
||||
vg_assert(VG_(threads)[tid].os_state.fatalsig != 0);
|
||||
VG_(kill_self)(VG_(threads)[tid].os_state.fatalsig);
|
||||
VG_(core_panic)("main(): signal was supposed to be fatal");
|
||||
break;
|
||||
|
||||
@ -73,9 +84,16 @@ static void terminate(ThreadId tid, VgSchedReturnCode src)
|
||||
}
|
||||
}
|
||||
|
||||
/* Run a thread from beginning to end. Does not return. */
|
||||
void VGA_(thread_wrapper)(Word /*ThreadId*/ tidW)
|
||||
|
||||
/* Run a thread from beginning to end and return the thread's
|
||||
scheduler-return-code. */
|
||||
|
||||
VgSchedReturnCode VGO_(thread_wrapper)(Word /*ThreadId*/ tidW)
|
||||
{
|
||||
VG_(debugLog)(1, "core_os",
|
||||
"VGO_(thread_wrapper)(tid=%lld): entry\n",
|
||||
(ULong)tidW);
|
||||
|
||||
VgSchedReturnCode ret;
|
||||
ThreadId tid = (ThreadId)tidW;
|
||||
ThreadState* tst = VG_(get_ThreadState)(tid);
|
||||
@ -103,79 +121,24 @@ void VGA_(thread_wrapper)(Word /*ThreadId*/ tidW)
|
||||
|
||||
vg_assert(tst->status == VgTs_Runnable);
|
||||
vg_assert(VG_(is_running_thread)(tid));
|
||||
|
||||
if (tid == VG_(master_tid)) {
|
||||
VG_(shutdown_actions)(tid);
|
||||
terminate(tid, ret);
|
||||
}
|
||||
|
||||
VG_(debugLog)(1, "core_os",
|
||||
"VGO_(thread_wrapper)(tid=%lld): done\n",
|
||||
(ULong)tidW);
|
||||
|
||||
/* Return to caller, still holding the lock. */
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* wait until all other threads are dead */
|
||||
static Bool alldead(void *v)
|
||||
{
|
||||
/* master_tid must be alive... */
|
||||
Int c = VG_(count_living_threads)();
|
||||
//VG_(printf)("alldead: count=%d\n", c);
|
||||
return c <= 1;
|
||||
}
|
||||
|
||||
static void sigvgchld_handler(Int sig)
|
||||
{
|
||||
VG_(printf)("got a sigvgchld?\n");
|
||||
}
|
||||
|
||||
/*
|
||||
Wait until some predicate about threadstates is satisfied.
|
||||
|
||||
This uses SIGVGCHLD as a notification that it is now worth
|
||||
re-evaluating the predicate.
|
||||
*/
|
||||
static void wait_for_threadstate(Bool (*pred)(void *), void *arg)
|
||||
{
|
||||
vki_sigset_t set, saved;
|
||||
struct vki_sigaction sa, old_sa;
|
||||
|
||||
/*
|
||||
SIGVGCHLD is set to be ignored, and is unblocked by default.
|
||||
This means all such signals are simply discarded.
|
||||
|
||||
In this loop, we actually block it, and then poll for it with
|
||||
sigtimedwait.
|
||||
*/
|
||||
VG_(sigemptyset)(&set);
|
||||
VG_(sigaddset)(&set, VKI_SIGVGCHLD);
|
||||
|
||||
VG_(set_sleeping)(VG_(master_tid), VgTs_Yielding);
|
||||
VG_(sigprocmask)(VKI_SIG_BLOCK, &set, &saved);
|
||||
|
||||
/* It shouldn't be necessary to set a handler, since the signal is
|
||||
always blocked, but it seems to be necessary to convice the
|
||||
kernel not to just toss the signal... */
|
||||
sa.ksa_handler = sigvgchld_handler;
|
||||
sa.sa_flags = 0;
|
||||
VG_(sigfillset)(&sa.sa_mask);
|
||||
VG_(sigaction)(VKI_SIGVGCHLD, &sa, &old_sa);
|
||||
|
||||
vg_assert(old_sa.ksa_handler == VKI_SIG_IGN);
|
||||
|
||||
while(!(*pred)(arg)) {
|
||||
struct vki_siginfo si;
|
||||
Int ret = VG_(sigtimedwait)(&set, &si, NULL);
|
||||
|
||||
if (ret > 0 && VG_(clo_trace_signals))
|
||||
VG_(message)(Vg_DebugMsg, "Got %d (code=%d) from tid lwp %d",
|
||||
ret, si.si_code, si._sifields._kill._pid);
|
||||
}
|
||||
|
||||
VG_(sigaction)(VKI_SIGVGCHLD, &old_sa, NULL);
|
||||
VG_(sigprocmask)(VKI_SIG_SETMASK, &saved, NULL);
|
||||
VG_(set_running)(VG_(master_tid));
|
||||
}
|
||||
|
||||
/* Wait until all other threads disappear. */
|
||||
void VGA_(reap_threads)(ThreadId self)
|
||||
{
|
||||
vg_assert(self == VG_(master_tid));
|
||||
wait_for_threadstate(alldead, NULL);
|
||||
while (!i_am_the_only_thread()) {
|
||||
/* Let other thread(s) run */
|
||||
VG_(vg_yield)();
|
||||
}
|
||||
vg_assert(i_am_the_only_thread());
|
||||
}
|
||||
|
||||
/* The we need to know the address of it so it can be
|
||||
|
||||
@ -35,6 +35,7 @@
|
||||
|
||||
#include "core.h"
|
||||
#include "ume.h" /* for jmp_with_stack */
|
||||
#include "pub_core_debuglog.h"
|
||||
#include "pub_core_aspacemgr.h"
|
||||
#include "pub_core_sigframe.h"
|
||||
#include "pub_core_syscalls.h"
|
||||
@ -244,64 +245,116 @@ SSizeT VGA_(stack_unused)(ThreadId tid)
|
||||
return ((Addr)p) - tst->os_state.valgrind_stack_base;
|
||||
}
|
||||
|
||||
/*
|
||||
Allocate a stack for the main thread, and call VGA_(thread_wrapper)
|
||||
on that stack.
|
||||
*/
|
||||
void VGA_(main_thread_wrapper)(ThreadId tid)
|
||||
|
||||
/* Run a thread all the way to the end, then do appropriate exit actions
|
||||
(this is the last-one-out-turn-off-the-lights bit).
|
||||
*/
|
||||
static void run_a_thread_NORETURN ( Word tidW )
|
||||
{
|
||||
ThreadId tid = (ThreadId)tidW;
|
||||
|
||||
VG_(debugLog)(1, "syscalls-x86-linux",
|
||||
"run_a_thread_NORETURN(tid=%lld): "
|
||||
"VGO_(thread_wrapper) called\n",
|
||||
(ULong)tidW);
|
||||
|
||||
/* Run the thread all the way through. */
|
||||
VgSchedReturnCode src = VGO_(thread_wrapper)(tid);
|
||||
|
||||
VG_(debugLog)(1, "syscalls-x86-linux",
|
||||
"run_a_thread_NORETURN(tid=%lld): "
|
||||
"VGO_(thread_wrapper) done\n",
|
||||
(ULong)tidW);
|
||||
|
||||
Int c = VG_(count_living_threads)();
|
||||
vg_assert(c >= 1); /* stay sane */
|
||||
|
||||
if (c == 1) {
|
||||
|
||||
VG_(debugLog)(1, "syscalls-x86-linux",
|
||||
"run_a_thread_NORETURN(tid=%lld): "
|
||||
"last one standing\n",
|
||||
(ULong)tidW);
|
||||
|
||||
/* We are the last one standing. Keep hold of the lock and
|
||||
carry on to show final tool results, then exit the entire system. */
|
||||
VG_(shutdown_actions_NORETURN)(tid, src);
|
||||
|
||||
} else {
|
||||
|
||||
VG_(debugLog)(1, "syscalls-x86-linux",
|
||||
"run_a_thread_NORETURN(tid=%lld): "
|
||||
"not last one standing\n",
|
||||
(ULong)tidW);
|
||||
|
||||
/* OK, thread is dead, but others still exist. Just exit. */
|
||||
ThreadState *tst = VG_(get_ThreadState)(tid);
|
||||
|
||||
/* This releases the run lock */
|
||||
VG_(exit_thread)(tid);
|
||||
vg_assert(tst->status == VgTs_Zombie);
|
||||
|
||||
/* We have to use this sequence to terminate the thread to
|
||||
prevent a subtle race. If VG_(exit_thread)() had left the
|
||||
ThreadState as Empty, then it could have been reallocated,
|
||||
reusing the stack while we're doing these last cleanups.
|
||||
Instead, VG_(exit_thread) leaves it as Zombie to prevent
|
||||
reallocation. We need to make sure we don't touch the stack
|
||||
between marking it Empty and exiting. Hence the
|
||||
assembler. */
|
||||
asm volatile (
|
||||
"movl %1, %0\n" /* set tst->status = VgTs_Empty */
|
||||
"movl %2, %%eax\n" /* set %eax = __NR_exit */
|
||||
"movl %3, %%ebx\n" /* set %ebx = tst->os_state.exitcode */
|
||||
"int $0x80\n" /* exit(tst->os_state.exitcode) */
|
||||
: "=m" (tst->status)
|
||||
: "n" (VgTs_Empty), "n" (__NR_exit), "m" (tst->os_state.exitcode));
|
||||
|
||||
VG_(core_panic)("Thread exit failed?\n");
|
||||
}
|
||||
|
||||
/*NOTREACHED*/
|
||||
vg_assert(0);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Allocate a stack for the main thread, and run it all the way to the
|
||||
end.
|
||||
*/
|
||||
void VGP_(main_thread_wrapper_NORETURN)(ThreadId tid)
|
||||
{
|
||||
VG_(debugLog)(1, "syscalls-x86-linux",
|
||||
"entering VGP_(main_thread_wrapper_NORETURN)\n");
|
||||
|
||||
UWord* esp = allocstack(tid);
|
||||
|
||||
vg_assert(tid == VG_(master_tid));
|
||||
/* shouldn't be any other threads around yet */
|
||||
vg_assert( VG_(count_living_threads)() == 1 );
|
||||
|
||||
call_on_new_stack_0_1(
|
||||
(Addr)esp, /* stack */
|
||||
0, /*bogus return address*/
|
||||
VGA_(thread_wrapper), /* fn to call */
|
||||
(Word)tid /* arg to give it */
|
||||
(Addr)esp, /* stack */
|
||||
0, /*bogus return address*/
|
||||
run_a_thread_NORETURN, /* fn to call */
|
||||
(Word)tid /* arg to give it */
|
||||
);
|
||||
|
||||
/*NOTREACHED*/
|
||||
vg_assert(0);
|
||||
}
|
||||
|
||||
static Int start_thread(void *arg)
|
||||
|
||||
static Int start_thread_NORETURN ( void* arg )
|
||||
{
|
||||
ThreadState *tst = (ThreadState *)arg;
|
||||
ThreadId tid = tst->tid;
|
||||
ThreadState* tst = (ThreadState*)arg;
|
||||
ThreadId tid = tst->tid;
|
||||
|
||||
VGA_(thread_wrapper)(tid);
|
||||
|
||||
/* OK, thread is dead; this releases the run lock */
|
||||
VG_(exit_thread)(tid);
|
||||
|
||||
vg_assert(tst->status == VgTs_Zombie);
|
||||
|
||||
/* Poke the reaper */
|
||||
if (VG_(clo_trace_signals))
|
||||
VG_(message)(Vg_DebugMsg, "Sending SIGVGCHLD to master tid=%d lwp=%d",
|
||||
VG_(master_tid), VG_(threads)[VG_(master_tid)].os_state.lwpid);
|
||||
|
||||
VG_(tkill)(VG_(threads)[VG_(master_tid)].os_state.lwpid, VKI_SIGVGCHLD);
|
||||
|
||||
/* We have to use this sequence to terminate the thread to prevent
|
||||
a subtle race. If VG_(exit_thread)() had left the ThreadState
|
||||
as Empty, then it could have been reallocated, reusing the stack
|
||||
while we're doing these last cleanups. Instead,
|
||||
VG_(exit_thread) leaves it as Zombie to prevent reallocation.
|
||||
We need to make sure we don't touch the stack between marking it
|
||||
Empty and exiting. Hence the assembler. */
|
||||
asm volatile (
|
||||
"movl %1, %0\n" /* set tst->status = VgTs_Empty */
|
||||
"movl %2, %%eax\n" /* set %eax = __NR_exit */
|
||||
"movl %3, %%ebx\n" /* set %ebx = tst->os_state.exitcode */
|
||||
"int $0x80\n" /* exit(tst->os_state.exitcode) */
|
||||
: "=m" (tst->status)
|
||||
: "n" (VgTs_Empty), "n" (__NR_exit), "m" (tst->os_state.exitcode));
|
||||
|
||||
VG_(core_panic)("Thread exit failed?\n");
|
||||
run_a_thread_NORETURN ( (Word)tid );
|
||||
/*NOTREACHED*/
|
||||
vg_assert(0);
|
||||
}
|
||||
|
||||
|
||||
/* ---------------------------------------------------------------------
|
||||
clone() handling
|
||||
------------------------------------------------------------------ */
|
||||
@ -404,7 +457,7 @@ static Int do_clone(ThreadId ptid,
|
||||
VG_(sigprocmask)(VKI_SIG_SETMASK, &blockall, &savedmask);
|
||||
|
||||
/* Create the new thread */
|
||||
ret = VG_(clone)(start_thread, stack, flags, &VG_(threads)[ctid],
|
||||
ret = VG_(clone)(start_thread_NORETURN, stack, flags, &VG_(threads)[ctid],
|
||||
child_tidptr, parent_tidptr, NULL);
|
||||
|
||||
VG_(sigprocmask)(VKI_SIG_SETMASK, &savedmask, NULL);
|
||||
@ -1470,7 +1523,7 @@ const struct SyscallTableEntry VGA_(syscall_table)[] = {
|
||||
GENX_(__NR_chroot, sys_chroot), // 61
|
||||
// (__NR_ustat, sys_ustat) // 62 SVr4 -- deprecated
|
||||
GENXY(__NR_dup2, sys_dup2), // 63
|
||||
GENXY(__NR_getppid, sys_getppid), // 64
|
||||
GENX_(__NR_getppid, sys_getppid), // 64
|
||||
|
||||
GENX_(__NR_getpgrp, sys_getpgrp), // 65
|
||||
GENX_(__NR_setsid, sys_setsid), // 66
|
||||
|
||||
@ -2345,27 +2345,17 @@ PRE(sys_execve, Special)
|
||||
/* Resistance is futile. Nuke all other threads. POSIX mandates
|
||||
this. (Really, nuke them all, since the new process will make
|
||||
its own new thread.) */
|
||||
VG_(master_tid) = tid; /* become the master */
|
||||
VG_(nuke_all_threads_except)( tid, VgSrc_ExitSyscall );
|
||||
VGA_(reap_threads)(tid);
|
||||
|
||||
if (0) {
|
||||
/* Shut down cleanly and report final state
|
||||
XXX Is this reasonable? */
|
||||
tst->exitreason = VgSrc_ExitSyscall;
|
||||
VG_(shutdown_actions)(tid);
|
||||
}
|
||||
|
||||
{
|
||||
// Remove the valgrind-specific stuff from the environment so the
|
||||
// child doesn't get vg_inject.so, vgpreload.so, etc. This is
|
||||
// done unconditionally, since if we are tracing the child,
|
||||
// stage1/2 will set up the appropriate client environment.
|
||||
Char** envp = (Char**)ARG3;
|
||||
|
||||
if (envp != NULL) {
|
||||
VG_(env_remove_valgrind_env_stuff)( envp );
|
||||
}
|
||||
{ // Remove the valgrind-specific stuff from the environment so the
|
||||
// child doesn't get vg_inject.so, vgpreload.so, etc. This is
|
||||
// done unconditionally, since if we are tracing the child,
|
||||
// stage1/2 will set up the appropriate client environment.
|
||||
Char** envp = (Char**)ARG3;
|
||||
if (envp != NULL) {
|
||||
VG_(env_remove_valgrind_env_stuff)( envp );
|
||||
}
|
||||
}
|
||||
|
||||
if (VG_(clo_trace_children)) {
|
||||
@ -2952,16 +2942,6 @@ PRE(sys_getppid, 0)
|
||||
PRE_REG_READ0(long, "getppid");
|
||||
}
|
||||
|
||||
POST(sys_getppid)
|
||||
{
|
||||
/* If the master thread has already exited, and it is this thread's
|
||||
parent, then force getppid to return 1 (init) rather than the
|
||||
real ppid, so that it thinks its parent has exited. */
|
||||
if (VG_(threads)[VG_(master_tid)].os_state.lwpid == RES &&
|
||||
VG_(is_exiting)(VG_(master_tid)))
|
||||
RES = 1;
|
||||
}
|
||||
|
||||
static void common_post_getrlimit(ThreadId tid, UWord a1, UWord a2)
|
||||
{
|
||||
POST_MEM_WRITE( a2, sizeof(struct vki_rlimit) );
|
||||
@ -6068,15 +6048,6 @@ static void sanitize_client_sigmask(ThreadId tid, vki_sigset_t *mask)
|
||||
VG_(sigdelset)(mask, VKI_SIGSTOP);
|
||||
|
||||
VG_(sigdelset)(mask, VKI_SIGVGKILL); /* never block */
|
||||
|
||||
/* SIGVGCHLD is used by threads to indicate their state changes to
|
||||
the master thread. Mostly it doesn't care, so it leaves the
|
||||
signal ignored and unblocked. Everyone else should have it
|
||||
blocked, so there's at most 1 thread with it unblocked. */
|
||||
if (tid == VG_(master_tid))
|
||||
VG_(sigdelset)(mask, VKI_SIGVGCHLD);
|
||||
else
|
||||
VG_(sigaddset)(mask, VKI_SIGVGCHLD);
|
||||
}
|
||||
|
||||
void VG_(client_syscall) ( ThreadId tid )
|
||||
|
||||
@ -137,12 +137,6 @@ const Char *VG_(libdir) = VG_LIBDIR;
|
||||
static Int vg_argc;
|
||||
static Char **vg_argv;
|
||||
|
||||
/* The master thread the one which will be responsible for mopping
|
||||
everything up at exit. Normally it is tid 1, since that's the
|
||||
first thread created, but it may be something else after a
|
||||
fork(). */
|
||||
ThreadId VG_(master_tid) = VG_INVALID_THREADID;
|
||||
|
||||
/* Application-visible file descriptor limits */
|
||||
Int VG_(fd_soft_limit) = -1;
|
||||
Int VG_(fd_hard_limit) = -1;
|
||||
@ -2817,24 +2811,29 @@ int main(int argc, char **argv, char **envp)
|
||||
//--------------------------------------------------------------
|
||||
VGP_POPCC(VgpStartup);
|
||||
|
||||
vg_assert(VG_(master_tid) == 1);
|
||||
|
||||
if (VG_(clo_xml)) {
|
||||
VG_(message)(Vg_UserMsg, "<status>RUNNING</status>");
|
||||
VG_(message)(Vg_UserMsg, "");
|
||||
}
|
||||
|
||||
VG_(debugLog)(1, "main", "Running thread 1\n");
|
||||
VGA_(main_thread_wrapper)(1);
|
||||
/* As a result of the following call, the last thread standing
|
||||
eventually winds up running VG_(shutdown_actions_NORETURN) just
|
||||
below. */
|
||||
VGP_(main_thread_wrapper_NORETURN)(1);
|
||||
|
||||
abort();
|
||||
/*NOTREACHED*/
|
||||
vg_assert(0);
|
||||
}
|
||||
|
||||
|
||||
/* Do everything which needs doing when the last thread exits */
|
||||
void VG_(shutdown_actions)(ThreadId tid)
|
||||
void VG_(shutdown_actions_NORETURN) ( ThreadId tid,
|
||||
VgSchedReturnCode tids_schedretcode )
|
||||
{
|
||||
vg_assert(tid == VG_(master_tid));
|
||||
VG_(debugLog)(1, "main", "entering VG_(shutdown_actions_NORETURN)\n");
|
||||
|
||||
vg_assert( VG_(count_living_threads)() == 1 );
|
||||
vg_assert(VG_(is_running_thread)(tid));
|
||||
|
||||
// Wait for all other threads to exit.
|
||||
@ -2896,6 +2895,12 @@ void VG_(shutdown_actions)(ThreadId tid)
|
||||
/* Print Vex storage stats */
|
||||
if (0)
|
||||
LibVEX_ShowAllocStats();
|
||||
|
||||
/* Ok, finally exit in the os-specific way. In short, if the
|
||||
(last) thread exited by calling sys_exit, do likewise; if the
|
||||
(last) thread stopped due to a fatal signal, terminate the
|
||||
entire system with that same fatal signal. */
|
||||
VGO_(terminate_NORETURN)( tid, tids_schedretcode );
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------*/
|
||||
|
||||
@ -439,10 +439,6 @@ static void block_signals(ThreadId tid)
|
||||
VG_(sigdelset)(&mask, VKI_SIGSTOP);
|
||||
VG_(sigdelset)(&mask, VKI_SIGKILL);
|
||||
|
||||
/* Master doesn't block this */
|
||||
if (tid == VG_(master_tid))
|
||||
VG_(sigdelset)(&mask, VKI_SIGVGCHLD);
|
||||
|
||||
VG_(sigprocmask)(VKI_SIG_SETMASK, &mask, NULL);
|
||||
}
|
||||
|
||||
@ -566,7 +562,7 @@ void mostly_clear_thread_record ( ThreadId tid )
|
||||
VG_(sigemptyset)(&VG_(threads)[tid].sig_mask);
|
||||
VG_(sigemptyset)(&VG_(threads)[tid].tmp_sig_mask);
|
||||
|
||||
VGA_(os_state_clear)(&VG_(threads)[tid]);
|
||||
VGO_(os_state_clear)(&VG_(threads)[tid]);
|
||||
|
||||
/* start with no altstack */
|
||||
VG_(threads)[tid].altstack.ss_sp = (void *)0xdeadbeef;
|
||||
@ -600,8 +596,6 @@ static void sched_fork_cleanup(ThreadId me)
|
||||
ThreadId tid;
|
||||
vg_assert(running_tid == me);
|
||||
|
||||
VG_(master_tid) = me;
|
||||
|
||||
VG_(threads)[me].os_state.lwpid = VG_(gettid)();
|
||||
VG_(threads)[me].os_state.threadgroup = VG_(getpid)();
|
||||
|
||||
@ -635,7 +629,7 @@ void VG_(scheduler_init) ( void )
|
||||
for (i = 0 /* NB; not 1 */; i < VG_N_THREADS; i++) {
|
||||
VG_(threads)[i].sig_queue = NULL;
|
||||
|
||||
VGA_(os_state_init)(&VG_(threads)[i]);
|
||||
VGO_(os_state_init)(&VG_(threads)[i]);
|
||||
mostly_clear_thread_record(i);
|
||||
|
||||
VG_(threads)[i].status = VgTs_Empty;
|
||||
@ -645,8 +639,6 @@ void VG_(scheduler_init) ( void )
|
||||
|
||||
tid_main = VG_(alloc_ThreadState)();
|
||||
|
||||
VG_(master_tid) = tid_main;
|
||||
|
||||
/* Initial thread's stack is the original process stack */
|
||||
VG_(threads)[tid_main].client_stack_highest_word
|
||||
= VG_(clstk_end) - sizeof(UWord);
|
||||
|
||||
@ -303,8 +303,6 @@ void calculate_SKSS_from_SCSS ( SKSS* dst )
|
||||
// cases in the switch, so we handle them in the 'default' case.
|
||||
if (sig == VKI_SIGVGKILL)
|
||||
skss_handler = sigvgkill_handler;
|
||||
else if (sig == VKI_SIGVGCHLD)
|
||||
skss_handler = VKI_SIG_IGN; /* we only poll for it */
|
||||
else {
|
||||
if (scss_handler == VKI_SIG_IGN)
|
||||
skss_handler = VKI_SIG_IGN;
|
||||
@ -1358,7 +1356,8 @@ static void default_action(const vki_siginfo_t *info, ThreadId tid)
|
||||
#endif
|
||||
|
||||
/* stash fatal signal in main thread */
|
||||
VG_(threads)[VG_(master_tid)].os_state.fatalsig = sigNo;
|
||||
// what's this for?
|
||||
//VG_(threads)[VG_(master_tid)].os_state.fatalsig = sigNo;
|
||||
|
||||
/* everyone dies */
|
||||
VG_(nuke_all_threads_except)(tid, VgSrc_FatalSig);
|
||||
@ -1884,8 +1883,10 @@ void sync_signalhandler ( Int sigNo, vki_siginfo_t *info, struct vki_ucontext *u
|
||||
if (0)
|
||||
VG_(kill_self)(sigNo); /* generate a core dump */
|
||||
|
||||
if (tid == 0) /* could happen after everyone has exited */
|
||||
tid = VG_(master_tid);
|
||||
//if (tid == 0) /* could happen after everyone has exited */
|
||||
// tid = VG_(master_tid);
|
||||
vg_assert(tid != 0);
|
||||
|
||||
tst = VG_(get_ThreadState)(tid);
|
||||
VG_(get_StackTrace2)(ips, VG_(clo_backtrace_size),
|
||||
VGP_UCONTEXT_INSTR_PTR(uc),
|
||||
@ -1967,8 +1968,6 @@ void VG_(poll_signals)(ThreadId tid)
|
||||
for(i = 0; i < _VKI_NSIG_WORDS; i++)
|
||||
pollset.sig[i] = ~tst->sig_mask.sig[i];
|
||||
|
||||
VG_(sigdelset)(&pollset, VKI_SIGVGCHLD); /* already dealt with */
|
||||
|
||||
//VG_(printf)("tid %d pollset=%08x%08x\n", tid, pollset.sig[1], pollset.sig[0]);
|
||||
|
||||
block_all_host_signals(&saved_mask); // protect signal queue
|
||||
@ -2063,18 +2062,17 @@ void VG_(sigstartup_actions) ( void )
|
||||
VG_(message)(Vg_DebugMsg, "Max kernel-supported signal is %d", VG_(max_signal));
|
||||
|
||||
/* Our private internal signals are treated as ignored */
|
||||
scss.scss_per_sig[VKI_SIGVGCHLD].scss_handler = VKI_SIG_IGN;
|
||||
scss.scss_per_sig[VKI_SIGVGCHLD].scss_flags = VKI_SA_SIGINFO;
|
||||
VG_(sigfillset)(&scss.scss_per_sig[VKI_SIGVGCHLD].scss_mask);
|
||||
|
||||
scss.scss_per_sig[VKI_SIGVGKILL].scss_handler = VKI_SIG_IGN;
|
||||
scss.scss_per_sig[VKI_SIGVGKILL].scss_flags = VKI_SA_SIGINFO;
|
||||
VG_(sigfillset)(&scss.scss_per_sig[VKI_SIGVGKILL].scss_mask);
|
||||
|
||||
/* Copy the process' signal mask into the root thread. */
|
||||
vg_assert(VG_(threads)[VG_(master_tid)].status == VgTs_Init);
|
||||
VG_(threads)[VG_(master_tid)].sig_mask = saved_procmask;
|
||||
VG_(threads)[VG_(master_tid)].tmp_sig_mask = saved_procmask;
|
||||
vg_assert(VG_(threads)[1].status == VgTs_Init);
|
||||
for (i = 2; i < VG_N_THREADS; i++)
|
||||
vg_assert(VG_(threads)[i].status == VgTs_Empty);
|
||||
|
||||
VG_(threads)[1].sig_mask = saved_procmask;
|
||||
VG_(threads)[1].tmp_sig_mask = saved_procmask;
|
||||
|
||||
/* Calculate SKSS and apply it. This also sets the initial kernel
|
||||
mask we need to run with. */
|
||||
|
||||
@ -351,8 +351,7 @@ struct vki_sched_param {
|
||||
|
||||
/* Use high signals because native pthreads wants to use low */
|
||||
#define VKI_SIGVGKILL (VG_(max_signal)-0) // [[internal: kill]]
|
||||
#define VKI_SIGVGCHLD (VG_(max_signal)-1) // [[internal: thread death]]
|
||||
#define VKI_SIGVGRTUSERMAX (VG_(max_signal)-2) // [[internal: last user-usable RT signal]]
|
||||
#define VKI_SIGVGRTUSERMAX (VG_(max_signal)-1) // [[internal: last user-usable RT signal]]
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// From linux-2.6.8.1/include/asm-generic/siginfo.h
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user