]> git.ipfire.org Git - thirdparty/valgrind.git/commitdiff
Change the way Valgrind exits.
authorJulian Seward <jseward@acm.org>
Fri, 3 May 2002 19:09:05 +0000 (19:09 +0000)
committerJulian Seward <jseward@acm.org>
Fri, 3 May 2002 19:09:05 +0000 (19:09 +0000)
Until now, valgrind waited for ld.so to call the .fini code in
valgrind.so, and took this as its cue to switch back to the real CPU
for the rest of the journey.

This is a problem if ld.so subsequently calls other .so's .fini code
and threading is in use, because they do pthread_* calls which cannot
be handled by valgrind's libpthread.so without valgrind actually being
active.

So we ignore the call to valgrind's .fini code, and run the program
all the way up to the point where it calls syscall exit() to
disappear.  This makes the order in which the .fini sections are run
irrelevant, since Valgrind has control during all of them, and so
threading facilities are still available for all of them.

This change means Mozilla 1.0RC1 now exits a lot more cleanly than it
did.

git-svn-id: svn://svn.valgrind.org/valgrind/trunk@201

coregrind/vg_constants.h
coregrind/vg_include.h
coregrind/vg_main.c
coregrind/vg_scheduler.c
coregrind/vg_startup.S
vg_constants.h
vg_include.h
vg_main.c
vg_scheduler.c
vg_startup.S
vg_syscall_mem.c

index 191e49889798e6507339352f40f004145a2d3c18..710b12cb90eab14e3cdb70da7573231d41125bdc 100644 (file)
 /* 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 */
 
index af23b40c5268cb07b74bf89297bee80a51b20829..4b36e443dabf971a36b410f157381b35c9822491 100644 (file)
@@ -437,7 +437,6 @@ extern Bool  VG_(is_empty_arena) ( ArenaId aid );
 In vg_constants.h:
 #define VG_USERREQ__SIGNAL_RETURNS          0x4001
 #define VG_USERREQ__PTHREAD_RETURNS         0x4002
-#define VG_USERREQ__SHUTDOWN_VALGRIND       0x4003
 */
 
 
@@ -602,9 +601,17 @@ extern ThreadId VG_(identify_stack_addr)( Addr a );
 
 /* 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 );
 
@@ -1320,6 +1327,9 @@ extern Bool VG_(running_on_simd_CPU); /* Initially False */
 /* 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. --- */
 
@@ -1549,7 +1559,6 @@ extern void VG_(do_syscall) ( void );
    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,
index b98459f34d54ed0e2e653b6b160e9e1bbf4f7c5e..1b6bba33be98896ad0369ba6aba1c6a7845d83d1 100644 (file)
@@ -331,6 +331,9 @@ Bool VG_(disassemble) = False;
 /* 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.
@@ -975,6 +978,7 @@ void VG_(main) ( void )
 {
    Int               i;
    VgSchedReturnCode src;
+   ThreadState*      tst;
 
    /* Set up our stack sanity-check words. */
    for (i = 0; i < 10; i++) {
@@ -1105,17 +1109,46 @@ void VG_(main) ( void )
       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");
+   }
 }
 
 
index a58558739d50bee0a7710fe491f9818c0edb82f6..ea24997adb56c35b0a2b9bf7755b98eb976aa83c 100644 (file)
@@ -1142,7 +1142,7 @@ VgSchedReturnCode VG_(scheduler) ( void )
 
    /* 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. */
@@ -1239,8 +1239,8 @@ VgSchedReturnCode VG_(scheduler) ( void )
             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"); */
       }
 
 
@@ -1275,6 +1275,8 @@ VgSchedReturnCode VG_(scheduler) ( void )
       /* Actually run thread tid. */
       while (True) {
 
+         VG_(last_run_tid) = tid;
+
          /* For stats purposes only. */
          VG_(num_scheduling_events_MINOR) ++;
 
@@ -1334,7 +1336,9 @@ VgSchedReturnCode VG_(scheduler) ( void )
 
          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;
@@ -1344,6 +1348,9 @@ VgSchedReturnCode VG_(scheduler) ( void )
             }
 #           endif
 
+            if (vg_threads[tid].m_eax == __NR_exit)
+               return VgSrc_ExitSyscall;
+
             sched_do_syscall(tid);
 
 #           if 0
@@ -1413,14 +1420,6 @@ VgSchedReturnCode VG_(scheduler) ( void )
                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
@@ -1442,11 +1441,7 @@ VgSchedReturnCode VG_(scheduler) ( void )
                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: 
index 561642196b904777d4c369fb054922f9c6b4986b..a0bb4eab79497b70379b7361059e9661999b5d6f 100644 (file)
@@ -104,40 +104,20 @@ first_insn_to_simulate:
 
 
 
-.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
index 191e49889798e6507339352f40f004145a2d3c18..710b12cb90eab14e3cdb70da7573231d41125bdc 100644 (file)
 /* 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 */
 
index af23b40c5268cb07b74bf89297bee80a51b20829..4b36e443dabf971a36b410f157381b35c9822491 100644 (file)
@@ -437,7 +437,6 @@ extern Bool  VG_(is_empty_arena) ( ArenaId aid );
 In vg_constants.h:
 #define VG_USERREQ__SIGNAL_RETURNS          0x4001
 #define VG_USERREQ__PTHREAD_RETURNS         0x4002
-#define VG_USERREQ__SHUTDOWN_VALGRIND       0x4003
 */
 
 
@@ -602,9 +601,17 @@ extern ThreadId VG_(identify_stack_addr)( Addr a );
 
 /* 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 );
 
@@ -1320,6 +1327,9 @@ extern Bool VG_(running_on_simd_CPU); /* Initially False */
 /* 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. --- */
 
@@ -1549,7 +1559,6 @@ extern void VG_(do_syscall) ( void );
    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,
index b98459f34d54ed0e2e653b6b160e9e1bbf4f7c5e..1b6bba33be98896ad0369ba6aba1c6a7845d83d1 100644 (file)
--- a/vg_main.c
+++ b/vg_main.c
@@ -331,6 +331,9 @@ Bool VG_(disassemble) = False;
 /* 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.
@@ -975,6 +978,7 @@ void VG_(main) ( void )
 {
    Int               i;
    VgSchedReturnCode src;
+   ThreadState*      tst;
 
    /* Set up our stack sanity-check words. */
    for (i = 0; i < 10; i++) {
@@ -1105,17 +1109,46 @@ void VG_(main) ( void )
       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");
+   }
 }
 
 
index a58558739d50bee0a7710fe491f9818c0edb82f6..ea24997adb56c35b0a2b9bf7755b98eb976aa83c 100644 (file)
@@ -1142,7 +1142,7 @@ VgSchedReturnCode VG_(scheduler) ( void )
 
    /* 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. */
@@ -1239,8 +1239,8 @@ VgSchedReturnCode VG_(scheduler) ( void )
             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"); */
       }
 
 
@@ -1275,6 +1275,8 @@ VgSchedReturnCode VG_(scheduler) ( void )
       /* Actually run thread tid. */
       while (True) {
 
+         VG_(last_run_tid) = tid;
+
          /* For stats purposes only. */
          VG_(num_scheduling_events_MINOR) ++;
 
@@ -1334,7 +1336,9 @@ VgSchedReturnCode VG_(scheduler) ( void )
 
          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;
@@ -1344,6 +1348,9 @@ VgSchedReturnCode VG_(scheduler) ( void )
             }
 #           endif
 
+            if (vg_threads[tid].m_eax == __NR_exit)
+               return VgSrc_ExitSyscall;
+
             sched_do_syscall(tid);
 
 #           if 0
@@ -1413,14 +1420,6 @@ VgSchedReturnCode VG_(scheduler) ( void )
                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
@@ -1442,11 +1441,7 @@ VgSchedReturnCode VG_(scheduler) ( void )
                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: 
index 561642196b904777d4c369fb054922f9c6b4986b..a0bb4eab79497b70379b7361059e9661999b5d6f 100644 (file)
@@ -104,40 +104,20 @@ first_insn_to_simulate:
 
 
 
-.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
index f3b7052f8aee38101d04fffd992afd11e26a6426..ab43451f22fe0d5b96e59f24c2cf4be8df09cec9 100644 (file)
@@ -332,6 +332,10 @@ void VG_(perform_assumed_nonblocking_syscall) ( ThreadId tid )
 
    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)");
@@ -348,7 +352,10 @@ void VG_(perform_assumed_nonblocking_syscall) ( ThreadId tid )
 #     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
 
@@ -715,18 +722,6 @@ void VG_(perform_assumed_nonblocking_syscall) ( ThreadId tid )
          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 */