/* Assembly code stubs make these requests ... */
#define VG_USERREQ__SIGNAL_RETURNS 0x4001
#define VG_USERREQ__PTHREAD_RETURNS 0x4002
-#define VG_USERREQ__SHUTDOWN_VALGRIND 0x4003
#endif /* ndef __VG_INCLUDE_H */
In vg_constants.h:
#define VG_USERREQ__SIGNAL_RETURNS 0x4001
#define VG_USERREQ__PTHREAD_RETURNS 0x4002
-#define VG_USERREQ__SHUTDOWN_VALGRIND 0x4003
*/
/* Return codes from the scheduler. */
typedef
- enum { VgSrc_Deadlock, VgSrc_Shutdown, VgSrc_BbsDone }
+ enum {
+ VgSrc_Deadlock, /* no runnable threads and no prospect of any
+ even if we wait for a long time */
+ VgSrc_ExitSyscall, /* client called exit(). This is the normal
+ route out. */
+ VgSrc_BbsDone /* In a debugging run, the specified number of
+ bbs has been completed. */
+ }
VgSchedReturnCode;
+
/* The scheduler. */
extern VgSchedReturnCode VG_(scheduler) ( void );
/* The current LRU epoch. */
extern UInt VG_(current_epoch);
+/* This is the ThreadId of the last thread the scheduler ran. */
+extern ThreadId VG_(last_run_tid);
+
/* --- Counters, for informational purposes only. --- */
Exports of vg_startup.S
------------------------------------------------------------------ */
-extern void VG_(shutdown);
extern void VG_(switch_to_real_CPU) ( void );
extern void VG_(swizzle_esp_then_start_GDB) ( Addr m_eip_at_error,
/* The current LRU epoch. */
UInt VG_(current_epoch) = 0;
+/* This is the ThreadId of the last thread the scheduler ran. */
+ThreadId VG_(last_run_tid) = 0;
+
/* ---------------------------------------------------------------------
Counters, for informational purposes only.
{
Int i;
VgSchedReturnCode src;
+ ThreadState* tst;
/* Set up our stack sanity-check words. */
for (i = 0; i < 10; i++) {
VG_(mash_LD_PRELOAD_string)(VG_(getenv)("LD_PRELOAD"));
}
- /* Prepare to restore state to the real CPU. */
- VG_(load_thread_state)(1 /* root thread */);
- VG_(copy_baseBlock_to_m_state_static)();
-
- /* This pushes a return address on the simulator's stack, which
- is abandoned. We call vg_sigshutdown_actions() at the end
- of vg_switch_to_real_CPU(), so as to ensure that the original
- stack and machine state is restored before the real signal
- mechanism is restored.
- */
- VG_(switch_to_real_CPU)();
+ /* Decide how to exit. This depends on what the scheduler
+ returned. */
+ switch (src) {
+ case VgSrc_ExitSyscall: /* the normal way out */
+ vg_assert(VG_(last_run_tid) > 0
+ && VG_(last_run_tid) < VG_N_THREADS);
+ tst = VG_(get_thread_state)(VG_(last_run_tid));
+ vg_assert(tst->status == VgTs_Runnable);
+ /* The thread's %EBX will hold the arg to exit(), so we just
+ do exit with that arg. */
+ VG_(exit)( tst->m_ebx );
+ /* NOT ALIVE HERE! */
+ VG_(panic)("entered the afterlife in vg_main() -- ExitSyscall");
+ break; /* what the hell :) */
+
+ case VgSrc_Deadlock:
+ /* Just exit now. No point in continuing. */
+ VG_(exit)(0);
+ VG_(panic)("entered the afterlife in vg_main() -- Deadlock");
+ break;
+
+ case VgSrc_BbsDone:
+ /* Tricky; we have to try and switch back to the real CPU.
+ This is all very dodgy and won't work at all in the
+ presence of threads, or if the client happened to be
+ running a signal handler. */
+ /* Prepare to restore state to the real CPU. */
+ VG_(load_thread_state)(1 /* root thread */ );
+ VG_(copy_baseBlock_to_m_state_static)();
+
+ /* This pushes a return address on the simulator's stack,
+ which is abandoned. We call vg_sigshutdown_actions() at
+ the end of vg_switch_to_real_CPU(), so as to ensure that
+ the original stack and machine state is restored before
+ the real signal mechanism is restored. */
+ VG_(switch_to_real_CPU)();
+
+ default:
+ VG_(panic)("vg_main(): unexpected scheduler return code");
+ }
}
/* Start with the root thread. tid in general indicates the
currently runnable/just-finished-running thread. */
- tid = 1;
+ VG_(last_run_tid) = tid = 1;
/* This is the top level scheduler loop. It falls into three
phases. */
while, and go round again, in the hope that eventually a
thread becomes runnable. */
nanosleep_for_a_while();
- // pp_sched_status();
- // VG_(printf)(".\n");
+ /* pp_sched_status(); */
+ /* VG_(printf)(".\n"); */
}
/* Actually run thread tid. */
while (True) {
+ VG_(last_run_tid) = tid;
+
/* For stats purposes only. */
VG_(num_scheduling_events_MINOR) ++;
if (trc == VG_TRC_EBP_JMP_SYSCALL) {
/* Do a syscall for the vthread tid. This could cause it
- to become non-runnable. */
+ to become non-runnable. One special case: spot the
+ client doing calls to exit() and take this as the cue
+ to exit. */
# if 0
{ UInt* esp; Int i;
esp=(UInt*)vg_threads[tid].m_esp;
}
# endif
+ if (vg_threads[tid].m_eax == __NR_exit)
+ return VgSrc_ExitSyscall;
+
sched_do_syscall(tid);
# if 0
1, whereupon the signal will be "delivered". */
break;
-#if 0
- case VG_TRC_EBP_JMP_SYSCALL:
- /* Do a syscall for the vthread tid. This could cause it
- to become non-runnable. */
- sched_do_syscall(tid);
- break;
-#endif
-
case VG_TRC_EBP_JMP_CLIENTREQ:
/* Do a client request for the vthread tid. Note that
some requests will have been handled by
other blocked threads become runnable. In general we
can and often do mess with the state of arbitrary
threads at this point. */
- if (request_code == VG_USERREQ__SHUTDOWN_VALGRIND) {
- return VgSrc_Shutdown;
- } else {
- do_nontrivial_clientreq(tid);
- }
+ do_nontrivial_clientreq(tid);
break;
default:
-.global VG_(shutdown)
VG_(shutdown):
- # ld.so will call here after execution of the program proper
- # is complete, to allow libraries to close down cleanly.
- # Note that we will enter here on the synthetic CPU, not
- # the real one! So the interpreter must notice when this
- # procedure is called, and use that as its cue to switch
- # back to the real CPU. As usual we have a client request
- # to do this. To make sense of this you need to read the
- # definition of VALGRIND_MAGIC_SEQUENCE in valgrind.h.
- pushl %eax
- pushl %edx
- subl $20, %esp # allocate arg block
- movl %esp, %eax # %eax == &_zzq_args[0]
- movl $VG_USERREQ__SHUTDOWN_VALGRIND, 0(%eax) # request
- # dont bother to fill in arg1 .. 4, not important
- # and now the magic sequence itself:
- roll $29, %eax
- roll $3, %eax
- rorl $27, %eax
- rorl $5, %eax
- roll $13, %eax
- roll $19, %eax
- # valgrind now exits. the following insns are
- # executed on the real CPU.
- addl $20, %esp
- popl %edx
- popl %eax
+ # Just return, and ignore any attempt by ld.so to call
+ # valgrind.sos exit function. We just run the client all
+ # the way to the final exit() syscall. This sidesteps
+ # problems caused by ld.so calling the finalisation code
+ # of other .sos *after* it shuts down valgrind, which
+ # was causing big problems with threads.
ret
+
+
.global VG_(switch_to_real_CPU)
VG_(switch_to_real_CPU):
- # Once Valgrind has decided it needs to exit, either
- # because it has detected a call to vg_shutdown, or
+ # Once Valgrind has decided it needs to exit,
# because the specified number of insns have been completed
# during a debugging run, it jumps here, which copies the
# simulators state into the real machine state. Execution
/* Assembly code stubs make these requests ... */
#define VG_USERREQ__SIGNAL_RETURNS 0x4001
#define VG_USERREQ__PTHREAD_RETURNS 0x4002
-#define VG_USERREQ__SHUTDOWN_VALGRIND 0x4003
#endif /* ndef __VG_INCLUDE_H */
In vg_constants.h:
#define VG_USERREQ__SIGNAL_RETURNS 0x4001
#define VG_USERREQ__PTHREAD_RETURNS 0x4002
-#define VG_USERREQ__SHUTDOWN_VALGRIND 0x4003
*/
/* Return codes from the scheduler. */
typedef
- enum { VgSrc_Deadlock, VgSrc_Shutdown, VgSrc_BbsDone }
+ enum {
+ VgSrc_Deadlock, /* no runnable threads and no prospect of any
+ even if we wait for a long time */
+ VgSrc_ExitSyscall, /* client called exit(). This is the normal
+ route out. */
+ VgSrc_BbsDone /* In a debugging run, the specified number of
+ bbs has been completed. */
+ }
VgSchedReturnCode;
+
/* The scheduler. */
extern VgSchedReturnCode VG_(scheduler) ( void );
/* The current LRU epoch. */
extern UInt VG_(current_epoch);
+/* This is the ThreadId of the last thread the scheduler ran. */
+extern ThreadId VG_(last_run_tid);
+
/* --- Counters, for informational purposes only. --- */
Exports of vg_startup.S
------------------------------------------------------------------ */
-extern void VG_(shutdown);
extern void VG_(switch_to_real_CPU) ( void );
extern void VG_(swizzle_esp_then_start_GDB) ( Addr m_eip_at_error,
/* The current LRU epoch. */
UInt VG_(current_epoch) = 0;
+/* This is the ThreadId of the last thread the scheduler ran. */
+ThreadId VG_(last_run_tid) = 0;
+
/* ---------------------------------------------------------------------
Counters, for informational purposes only.
{
Int i;
VgSchedReturnCode src;
+ ThreadState* tst;
/* Set up our stack sanity-check words. */
for (i = 0; i < 10; i++) {
VG_(mash_LD_PRELOAD_string)(VG_(getenv)("LD_PRELOAD"));
}
- /* Prepare to restore state to the real CPU. */
- VG_(load_thread_state)(1 /* root thread */);
- VG_(copy_baseBlock_to_m_state_static)();
-
- /* This pushes a return address on the simulator's stack, which
- is abandoned. We call vg_sigshutdown_actions() at the end
- of vg_switch_to_real_CPU(), so as to ensure that the original
- stack and machine state is restored before the real signal
- mechanism is restored.
- */
- VG_(switch_to_real_CPU)();
+ /* Decide how to exit. This depends on what the scheduler
+ returned. */
+ switch (src) {
+ case VgSrc_ExitSyscall: /* the normal way out */
+ vg_assert(VG_(last_run_tid) > 0
+ && VG_(last_run_tid) < VG_N_THREADS);
+ tst = VG_(get_thread_state)(VG_(last_run_tid));
+ vg_assert(tst->status == VgTs_Runnable);
+ /* The thread's %EBX will hold the arg to exit(), so we just
+ do exit with that arg. */
+ VG_(exit)( tst->m_ebx );
+ /* NOT ALIVE HERE! */
+ VG_(panic)("entered the afterlife in vg_main() -- ExitSyscall");
+ break; /* what the hell :) */
+
+ case VgSrc_Deadlock:
+ /* Just exit now. No point in continuing. */
+ VG_(exit)(0);
+ VG_(panic)("entered the afterlife in vg_main() -- Deadlock");
+ break;
+
+ case VgSrc_BbsDone:
+ /* Tricky; we have to try and switch back to the real CPU.
+ This is all very dodgy and won't work at all in the
+ presence of threads, or if the client happened to be
+ running a signal handler. */
+ /* Prepare to restore state to the real CPU. */
+ VG_(load_thread_state)(1 /* root thread */ );
+ VG_(copy_baseBlock_to_m_state_static)();
+
+ /* This pushes a return address on the simulator's stack,
+ which is abandoned. We call vg_sigshutdown_actions() at
+ the end of vg_switch_to_real_CPU(), so as to ensure that
+ the original stack and machine state is restored before
+ the real signal mechanism is restored. */
+ VG_(switch_to_real_CPU)();
+
+ default:
+ VG_(panic)("vg_main(): unexpected scheduler return code");
+ }
}
/* Start with the root thread. tid in general indicates the
currently runnable/just-finished-running thread. */
- tid = 1;
+ VG_(last_run_tid) = tid = 1;
/* This is the top level scheduler loop. It falls into three
phases. */
while, and go round again, in the hope that eventually a
thread becomes runnable. */
nanosleep_for_a_while();
- // pp_sched_status();
- // VG_(printf)(".\n");
+ /* pp_sched_status(); */
+ /* VG_(printf)(".\n"); */
}
/* Actually run thread tid. */
while (True) {
+ VG_(last_run_tid) = tid;
+
/* For stats purposes only. */
VG_(num_scheduling_events_MINOR) ++;
if (trc == VG_TRC_EBP_JMP_SYSCALL) {
/* Do a syscall for the vthread tid. This could cause it
- to become non-runnable. */
+ to become non-runnable. One special case: spot the
+ client doing calls to exit() and take this as the cue
+ to exit. */
# if 0
{ UInt* esp; Int i;
esp=(UInt*)vg_threads[tid].m_esp;
}
# endif
+ if (vg_threads[tid].m_eax == __NR_exit)
+ return VgSrc_ExitSyscall;
+
sched_do_syscall(tid);
# if 0
1, whereupon the signal will be "delivered". */
break;
-#if 0
- case VG_TRC_EBP_JMP_SYSCALL:
- /* Do a syscall for the vthread tid. This could cause it
- to become non-runnable. */
- sched_do_syscall(tid);
- break;
-#endif
-
case VG_TRC_EBP_JMP_CLIENTREQ:
/* Do a client request for the vthread tid. Note that
some requests will have been handled by
other blocked threads become runnable. In general we
can and often do mess with the state of arbitrary
threads at this point. */
- if (request_code == VG_USERREQ__SHUTDOWN_VALGRIND) {
- return VgSrc_Shutdown;
- } else {
- do_nontrivial_clientreq(tid);
- }
+ do_nontrivial_clientreq(tid);
break;
default:
-.global VG_(shutdown)
VG_(shutdown):
- # ld.so will call here after execution of the program proper
- # is complete, to allow libraries to close down cleanly.
- # Note that we will enter here on the synthetic CPU, not
- # the real one! So the interpreter must notice when this
- # procedure is called, and use that as its cue to switch
- # back to the real CPU. As usual we have a client request
- # to do this. To make sense of this you need to read the
- # definition of VALGRIND_MAGIC_SEQUENCE in valgrind.h.
- pushl %eax
- pushl %edx
- subl $20, %esp # allocate arg block
- movl %esp, %eax # %eax == &_zzq_args[0]
- movl $VG_USERREQ__SHUTDOWN_VALGRIND, 0(%eax) # request
- # dont bother to fill in arg1 .. 4, not important
- # and now the magic sequence itself:
- roll $29, %eax
- roll $3, %eax
- rorl $27, %eax
- rorl $5, %eax
- roll $13, %eax
- roll $19, %eax
- # valgrind now exits. the following insns are
- # executed on the real CPU.
- addl $20, %esp
- popl %edx
- popl %eax
+ # Just return, and ignore any attempt by ld.so to call
+ # valgrind.sos exit function. We just run the client all
+ # the way to the final exit() syscall. This sidesteps
+ # problems caused by ld.so calling the finalisation code
+ # of other .sos *after* it shuts down valgrind, which
+ # was causing big problems with threads.
ret
+
+
.global VG_(switch_to_real_CPU)
VG_(switch_to_real_CPU):
- # Once Valgrind has decided it needs to exit, either
- # because it has detected a call to vg_shutdown, or
+ # Once Valgrind has decided it needs to exit,
# because the specified number of insns have been completed
# during a debugging run, it jumps here, which copies the
# simulators state into the real machine state. Execution
switch (syscallno) {
+ case __NR_exit:
+ VG_(panic)("syscall exit() not caught by the scheduler?!");
+ break;
+
case __NR_sigaltstack:
VG_(unimplemented)
("client signals on alternative stack (SA_ONSTACK)");
# if defined(__NR_modify_ldt)
case __NR_modify_ldt:
VG_(unimplemented)
- ("modify_ldt(): I (JRS) haven't investigated this yet; sorry.");
+ ("modify_ldt(): I (JRS) haven't investigated this yet; sorry.\n "
+ "This might be caused by linking to NVidia's libGL.so, so\n "
+ "avoiding it, if you can, _might_ help you. For example,\n "
+ "re-build any Qt libraries you are using without OpenGL support.");
break;
# endif
vg_assert(VG_(is_kerror)(res));
break;
- case __NR_exit: /* syscall 1 */
- /* void _exit(int status); */
- if (VG_(clo_trace_syscalls))
- VG_(printf)("exit ( %d )\n", arg1);
- VG_(message)(Vg_UserMsg,
- "Warning: client exiting by calling exit(%d). Bye!",
- arg1);
-
- KERNEL_DO_SYSCALL(tid,res);
- /* Definitely should not be alive here :) */
- break;
-
/* !!!!!!!!!!!!!!!!!!!!! end !!!!!!!!!!!!!!!!!!!!! */
case __NR_access: /* syscall 33 */