Merge in changes from the 2.4.0 line.  This basically brings in the
overhaul of the thread support.  Many things are now probably broken,
but at least with --tool=none, simple and not-so-simple threaded and
non-thread programs work.



git-svn-id: svn://svn.valgrind.org/valgrind/trunk@3265 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/coregrind/vg_main.c b/coregrind/vg_main.c
index 46d3683..3048e8d 100644
--- a/coregrind/vg_main.c
+++ b/coregrind/vg_main.c
@@ -45,6 +45,8 @@
 #include <sys/wait.h>
 #include <unistd.h>
 
+#include "memcheck/memcheck.h"
+
 #ifndef AT_DCACHEBSIZE
 #define AT_DCACHEBSIZE		19
 #endif /* AT_DCACHEBSIZE */
@@ -122,11 +124,11 @@
 static Int  vg_argc;
 static Char **vg_argv;
 
-/* PID of the main thread */
-Int VG_(main_pid);
-
-/* PGRP of process */
-Int VG_(main_pgrp);
+/* 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;
@@ -191,6 +193,8 @@
       VG_(sanity_check_malloc_all)();
       VG_(print_all_arena_stats)();
       VG_(message)(Vg_DebugMsg, "");
+      //VG_(print_shadow_stats)();
+      VG_(message)(Vg_DebugMsg, "");
       VG_(message)(Vg_DebugMsg, 
          "------ Valgrind's ExeContext management stats follow ------" );
       VG_(print_ExeContext_stats)();
@@ -326,21 +330,12 @@
    OINK(n);
 }
 
-/* Initialize the PID and PGRP of scheduler LWP; this is also called
-   in any new children after fork. */
-static void newpid(ThreadId unused)
-{
-   /* PID of scheduler LWP */
-   VG_(main_pid)  = VG_(getpid)();
-   VG_(main_pgrp) = VG_(getpgrp)();
-}
-
 /*====================================================================*/
 /*=== Check we were launched by stage 1                            ===*/
 /*====================================================================*/
 
 /* Look for our AUXV table */
-int scan_auxv(void* init_sp)
+static int scan_auxv(void* init_sp)
 {
    const struct ume_auxv *auxv = find_auxv((UWord*)init_sp);
    int padfile = -1, found = 0;
@@ -641,23 +636,29 @@
       cl_argv = argv;
 
    } else {
+      Bool noaugment = False;
+
       /* Count the arguments on the command line. */
       vg_argv0 = argv;
 
       for (vg_argc0 = 1; vg_argc0 < argc; vg_argc0++) {
-	 if (argv[vg_argc0][0] != '-') /* exe name */
+         Char* arg = argv[vg_argc0];
+         if (arg[0] != '-') /* exe name */
 	    break;
-	 if (VG_STREQ(argv[vg_argc0], "--")) { /* dummy arg */
+	 if (VG_STREQ(arg, "--")) { /* dummy arg */
 	    vg_argc0++;
 	    break;
 	 }
+         VG_BOOL_CLO("--command-line-only", noaugment)
       }
       cl_argv = &argv[vg_argc0];
 
       /* Get extra args from VALGRIND_OPTS and .valgrindrc files.
          Note we don't do this if getting args from VALGRINDCLO, as 
-         those extra args will already be present in VALGRINDCLO. */
-      augment_command_line(&vg_argc0, &vg_argv0);
+         those extra args will already be present in VALGRINDCLO.
+         (We also don't do it when --command-line-only=yes.) */
+      if (!noaugment)
+	 augment_command_line(&vg_argc0, &vg_argv0);
    }
 
    if (0) {
@@ -715,19 +716,11 @@
    return False;
 }
 
-static Bool contains(const char *p) {
-   if (VG_STREQ(p, VG_(libdir))) {
-      return True;
-   }
-   return False;
-}
-
 /* Prepare the client's environment.  This is basically a copy of our
    environment, except:
-   1. LD_LIBRARY_PATH=$VALGRINDLIB:$LD_LIBRARY_PATH
-   2. LD_PRELOAD=$VALGRINDLIB/vg_inject.so:($VALGRINDLIB/vgpreload_TOOL.so:)?$LD_PRELOAD
+     LD_PRELOAD=$VALGRINDLIB/vg_inject.so:($VALGRINDLIB/vgpreload_TOOL.so:)?$LD_PRELOAD
 
-   If any of these is missing, then it is added.
+   If this is missing, then it is added.
 
    Yummy.  String hacking in C.
 
@@ -737,14 +730,11 @@
 static char **fix_environment(char **origenv, const char *preload)
 {
    static const char inject_so[]          = "vg_inject.so";
-   static const char ld_library_path[]    = "LD_LIBRARY_PATH=";
    static const char ld_preload[]         = "LD_PRELOAD=";
    static const char valgrind_clo[]       = VALGRINDCLO "=";
-   static const int  ld_library_path_len  = sizeof(ld_library_path)-1;
    static const int  ld_preload_len       = sizeof(ld_preload)-1;
    static const int  valgrind_clo_len     = sizeof(valgrind_clo)-1;
    int ld_preload_done       = 0;
-   int ld_library_path_done  = 0;
    char *inject_path;
    int   inject_path_len;
    int vgliblen = strlen(VG_(libdir));
@@ -772,7 +762,7 @@
       envc++;
 
    /* Allocate a new space */
-   ret = malloc(sizeof(char *) * (envc+3+1)); /* 3 new entries + NULL */
+   ret = malloc(sizeof(char *) * (envc+1+1)); /* 1 new entry + NULL */
    vg_assert(ret);
 
    /* copy it over */
@@ -784,25 +774,7 @@
 
    /* Walk over the new environment, mashing as we go */
    for (cpp = ret; cpp && *cpp; cpp++) {
-      if (memcmp(*cpp, ld_library_path, ld_library_path_len) == 0) {
-	 /* If the LD_LIBRARY_PATH already contains libdir, then don't
-	    bother adding it again, even if it isn't the first (it
-	    seems that the Java runtime will keep reexecing itself
-	    unless its paths are at the front of LD_LIBRARY_PATH) */
-         if (!scan_colsep(*cpp + ld_library_path_len, contains)) {
-	    int len = strlen(*cpp) + vgliblen*2 + 16;
-	    char *cp = malloc(len);
-            vg_assert(cp);
-
-	    snprintf(cp, len, "%s%s:%s",
-		     ld_library_path, VG_(libdir),
-		     (*cpp)+ld_library_path_len);
-
-	    *cpp = cp;
-	 }
-
-	 ld_library_path_done = 1;
-      } else if (memcmp(*cpp, ld_preload, ld_preload_len) == 0) {
+      if (memcmp(*cpp, ld_preload, ld_preload_len) == 0) {
 	 int len = strlen(*cpp) + inject_path_len;
 	 char *cp = malloc(len);
          vg_assert(cp);
@@ -819,17 +791,6 @@
    }
 
    /* Add the missing bits */
-
-   if (!ld_library_path_done) {
-      int len = ld_library_path_len + vgliblen*2 + 16;
-      char *cp = malloc(len);
-      vg_assert(cp);
-
-      snprintf(cp, len, "%s%s", ld_library_path, VG_(libdir));
-
-      ret[envc++] = cp;
-   }
-
    if (!ld_preload_done) {
       int len = ld_preload_len + inject_path_len;
       char *cp = malloc(len);
@@ -841,13 +802,13 @@
       ret[envc++] = cp;
    }
 
+   free(inject_path);
    ret[envc] = NULL;
 
    return ret;
 }
 
 extern char **environ;		/* our environment */
-//#include <error.h>
 
 /* Add a string onto the string table, and return its address */
 static char *copy_str(char **tab, const char *str)
@@ -1053,10 +1014,7 @@
 	 break;
 
       case AT_BASE:
-	 if (info->interp_base == 0)
-	    auxv->a_type = AT_IGNORE;
-	 else
-	    auxv->u.a_val = info->interp_base;
+	 auxv->u.a_val = info->interp_base;
 	 break;
 
       case AT_PLATFORM:		/* points to a platform description string */
@@ -1092,7 +1050,7 @@
 	    suid, and therefore the dynamic linker should be careful
 	    about LD_PRELOAD, etc.  However, since stage1 (the thing
 	    the kernel actually execve's) should never be SUID, and we
-	    need LD_PRELOAD/LD_LIBRARY_PATH to work for the client, we
+	    need LD_PRELOAD to work for the client, we
 	    set AT_SECURE to 0. */
 	 auxv->u.a_val = 0;
 	 break;
@@ -1219,7 +1177,7 @@
                        ToolInfo** toolinfo_out, char **preloadpath_out )
 {
    Bool      ok;
-   int       len = strlen(VG_(libdir)) + strlen(toolname)*2 + 16;
+   int       len = strlen(VG_(libdir)) + strlen(toolname) + 16;
    char      buf[len];
    void*     handle;
    ToolInfo* toolinfo;
@@ -1324,15 +1282,6 @@
    VG_(exit)(1);
 }
 
-static void missing_tool_option ( void  )
-{
-   abort_msg();
-   VG_(printf)("valgrind: Missing --tool option\n");
-   list_tools();
-   VG_(printf)("valgrind: Use --help for more information.\n");
-   VG_(exit)(1);
-}
-
 static void missing_prog ( void  )
 {
    abort_msg();
@@ -1441,7 +1390,7 @@
    killpad_extra extra;
    int res;
 
-   vg_assert(padfile > 0);
+   vg_assert(padfile >= 0);
    
    res = fstat(padfile, &padstat);
    vg_assert(0 == res);
@@ -1492,10 +1441,11 @@
 Bool   VG_(clo_trace_syscalls) = False;
 Bool   VG_(clo_trace_signals)  = False;
 Bool   VG_(clo_trace_symtab)   = False;
+Bool   VG_(clo_trace_redir)    = False;
 Bool   VG_(clo_trace_sched)    = False;
-Int    VG_(clo_trace_pthread_level) = 0;
+Bool   VG_(clo_trace_pthreads) = False;
 Int    VG_(clo_dump_error)     = 0;
-Int    VG_(clo_backtrace_size) = 4;
+Int    VG_(clo_backtrace_size) = 12;
 Char*  VG_(clo_weird_hacks)    = NULL;
 Bool   VG_(clo_run_libc_freeres) = True;
 Bool   VG_(clo_track_fds)      = False;
@@ -1503,18 +1453,10 @@
 Bool   VG_(clo_pointercheck)   = True;
 Bool   VG_(clo_support_elan3)  = False;
 Bool   VG_(clo_branchpred)     = False;
+Bool   VG_(clo_model_pthreads) = False;
 
 static Bool   VG_(clo_wait_for_gdb)   = False;
 
-/* If we're doing signal routing, poll for signals every 50mS by
-   default. */
-Int    VG_(clo_signal_polltime) = 50;
-
-/* These flags reduce thread wakeup latency on syscall completion and
-   signal delivery, respectively.  The downside is possible unfairness. */
-Bool   VG_(clo_lowlat_syscalls) = False; /* low-latency syscalls */
-Bool   VG_(clo_lowlat_signals)  = False; /* low-latency signals */
-
 
 void usage ( Bool debug_help )
 {
@@ -1522,7 +1464,7 @@
 "usage: valgrind --tool=<toolname> [options] prog-and-args\n"
 "\n"
 "  common user options for all Valgrind tools, with defaults in [ ]:\n"
-"    --tool=<name>             use the Valgrind tool named <name>\n"
+"    --tool=<name>             use the Valgrind tool named <name> [memcheck]\n"
 "    -h --help                 show this message\n"
 "    --help-debug              show this message, plus debugging options\n"
 "    --version                 show version\n"
@@ -1534,10 +1476,7 @@
 "\n"
 "  uncommon user options for all Valgrind tools:\n"
 "    --run-libc-freeres=no|yes free up glibc memory at exit? [yes]\n"
-"    --weird-hacks=hack1,hack2,...  recognised hacks: lax-ioctls [none]\n"
-"    --signal-polltime=<time>  signal poll period (mS) for older kernels [50]\n"
-"    --lowlat-signals=no|yes   improve thread signal wake-up latency [no]\n"
-"    --lowlat-syscalls=no|yes  improve thread syscall wake-up latency [no]\n"
+"    --weird-hacks=hack1,hack2,...  recognised hacks: lax-ioctls,ioctl-mmap [none]\n"
 "    --pointercheck=no|yes     enforce client address space limits [yes]\n"
 "    --support-elan3=no|yes    hacks for Quadrics Elan3 support [no]\n"
 "\n"
@@ -1572,12 +1511,15 @@
 "    --trace-signals=no|yes    show signal handling details? [no]\n"
 "    --trace-symtab=no|yes     show symbol table details? [no]\n"
 "    --trace-sched=no|yes      show thread scheduler details? [no]\n"
-"    --trace-pthread=none|some|all  show pthread event details? [none]\n"
 "    --wait-for-gdb=yes|no     pause on startup to wait for gdb attach\n"
 "\n"
 "    --vex-iropt-verbosity             0 .. 9 [0]\n"
 "    --vex-iropt-level                 0 .. 2 [2]\n"
 "    --vex-iropt-precise-memory-exns   [no]\n"
+#if 0
+"    --model-pthreads=yes|no   model the pthreads library [no]\n"
+#endif
+"    --command-line-only=no|yes  only use command line options [no]\n"
 "    --vex-iropt-unroll-thresh         0 .. 400 [120]\n"
 "    --vex-guest-max-insns             1 .. 100 [50]\n"
 "    --vex-guest-chase-thresh          0 .. 99  [10]\n"
@@ -1661,17 +1603,6 @@
 	 *exec = &vg_argv[i][7];
       }
    }
-
-   /* If no tool specified, can act appropriately without loading tool */
-   if (*tool == NULL) {
-      if (0 == *need_help) {
-         // neither --tool nor --help/--help-debug specified
-         missing_tool_option();
-      } else {
-         // Give help message, without any tool-specific help
-         usage(/*help-debug?*/2 == *need_help);
-      }
-   }
 }
 
 static void process_cmd_line_options( UInt* client_auxv, const char* toolname )
@@ -1690,7 +1621,7 @@
 // XXX: what architectures is this necessary for?  x86 yes, PPC no, others ?
 #ifdef __x86__
    {
-      Word *auxp;
+      UInt* auxp;
       for (auxp = client_auxv; auxp[0] != AT_NULL; auxp += 2) {
          switch(auxp[0]) {
          case AT_SYSINFO:
@@ -1734,6 +1665,8 @@
 	 continue;
       if (VG_CLO_STREQN(7, arg, "--exec="))
 	 continue;
+      if (VG_CLO_STREQN(20, arg, "--command-line-only="))
+	 continue;
 
       if (     VG_CLO_STREQ(arg, "--"))
 	 continue;
@@ -1750,8 +1683,6 @@
       else VG_BOOL_CLO("--db-attach",        VG_(clo_db_attach))
       else VG_BOOL_CLO("--demangle",         VG_(clo_demangle))
       else VG_BOOL_CLO("--error-limit",      VG_(clo_error_limit))
-      else VG_BOOL_CLO("--lowlat-signals",   VG_(clo_lowlat_signals))
-      else VG_BOOL_CLO("--lowlat-syscalls",  VG_(clo_lowlat_syscalls))
       else VG_BOOL_CLO("--pointercheck",     VG_(clo_pointercheck))
       else VG_BOOL_CLO("--support-elan3",    VG_(clo_support_elan3))
       else VG_BOOL_CLO("--profile",          VG_(clo_profile))
@@ -1763,8 +1694,11 @@
       else VG_BOOL_CLO("--trace-sched",      VG_(clo_trace_sched))
       else VG_BOOL_CLO("--trace-signals",    VG_(clo_trace_signals))
       else VG_BOOL_CLO("--trace-symtab",     VG_(clo_trace_symtab))
+      else VG_BOOL_CLO("--trace-redir",      VG_(clo_trace_redir))
       else VG_BOOL_CLO("--trace-syscalls",   VG_(clo_trace_syscalls))
+      else VG_BOOL_CLO("--trace-pthreads",   VG_(clo_trace_pthreads))
       else VG_BOOL_CLO("--wait-for-gdb",     VG_(clo_wait_for_gdb))
+      else VG_BOOL_CLO("--model-pthreads",   VG_(clo_model_pthreads))
 
       else VG_STR_CLO ("--db-command",        VG_(clo_db_command))
       else VG_STR_CLO ("--weird-hacks",       VG_(clo_weird_hacks))
@@ -1772,7 +1706,6 @@
       else VG_NUM_CLO ("--dump-error",        VG_(clo_dump_error))
       else VG_NUM_CLO ("--input-fd",          VG_(clo_input_fd))
       else VG_NUM_CLO ("--sanity-level",      VG_(clo_sanity_level))
-      else VG_NUM_CLO ("--signal-polltime",   VG_(clo_signal_polltime))
       else VG_BNUM_CLO("--num-callers",       VG_(clo_backtrace_size), 1,
                                                 VG_DEEPEST_BACKTRACE)
 
@@ -1880,13 +1813,6 @@
 
       else VG_NUM_CLO ("--trace-notbelow",        VG_(clo_trace_notbelow))
 
-      else if (VG_CLO_STREQ(arg, "--trace-pthread=none"))
-         VG_(clo_trace_pthread_level) = 0;
-      else if (VG_CLO_STREQ(arg, "--trace-pthread=some"))
-         VG_(clo_trace_pthread_level) = 1;
-      else if (VG_CLO_STREQ(arg, "--trace-pthread=all"))
-         VG_(clo_trace_pthread_level) = 2;
-
       else if (VG_CLO_STREQ(arg, "--gen-suppressions=no"))
          VG_(clo_gen_suppressions) = 0;
       else if (VG_CLO_STREQ(arg, "--gen-suppressions=yes"))
@@ -2246,35 +2172,44 @@
 /*===  Initialise program data/text, etc.                          ===*/
 /*====================================================================*/
 
-static void build_valgrind_map_callback 
-      ( Addr start, SizeT size, Char rr, Char ww, Char xx, 
-        UInt dev, UInt ino, ULong foffset, const UChar* filename )
+static void build_valgrind_map_callback ( Addr start, SizeT size, UInt prot,
+					  UInt dev, UInt ino, ULong foffset, 
+					  const UChar* filename )
 {
-   UInt prot  = 0;
-   UInt flags = SF_MMAP|SF_NOSYMS;
-   Bool is_stack_segment;
-
-   is_stack_segment = 
-      (start == VG_(clstk_base) && (start+size) == VG_(clstk_end));
-
    /* Only record valgrind mappings for now, without loading any
       symbols.  This is so we know where the free space is before we
       start allocating more memory (note: heap is OK, it's just mmap
       which is the problem here). */
-   if (start >= VG_(valgrind_base) && (start+size-1) <= VG_(valgrind_last)) {
-      flags |= SF_VALGRIND;
-      VG_(map_file_segment)(start, size, prot, flags, dev, ino, foffset, filename);
+   if (start >= VG_(client_end) && start < VG_(valgrind_last)) {
+      if (0)
+	 VG_(printf)("init1: %p-%p prot %s\n",
+		     start, start+size, VG_(prot_str)(prot));
+      VG_(map_file_segment)(start, size, prot,
+			    SF_MMAP|SF_NOSYMS|SF_VALGRIND,
+			    dev, ino, foffset, filename);
+      /* update VG_(valgrind_last) if it looks wrong */
+      if (start+size > VG_(valgrind_last))
+	      VG_(valgrind_last) = start+size-1;
    }
 }
 
 // Global var used to pass local data to callback
 Addr sp_at_startup___global_arg = 0;
 
-static void build_segment_map_callback 
-      ( Addr start, SizeT size, Char rr, Char ww, Char xx,
-        UInt dev, UInt ino, ULong foffset, const UChar* filename )
+/* 
+   This second pass adds in client mappings, and loads symbol tables
+   for all interesting mappings.  The trouble is that things can
+   change as we go, because we're calling the Tool to track memory as
+   we find it.
+
+   So for Valgrind mappings, we don't replace any mappings which
+   aren't still identical (which will include the .so mappings, so we
+   will load their symtabs)>
+ */
+static void build_segment_map_callback ( Addr start, SizeT size, UInt prot,
+					 UInt dev, UInt ino, ULong foffset,
+					 const UChar* filename )
 {
-   UInt prot = 0;
    UInt flags;
    Bool is_stack_segment;
    Addr r_esp;
@@ -2282,9 +2217,9 @@
    is_stack_segment 
       = (start == VG_(clstk_base) && (start+size) == VG_(clstk_end));
 
-   if (rr == 'r') prot |= VKI_PROT_READ;
-   if (ww == 'w') prot |= VKI_PROT_WRITE;
-   if (xx == 'x') prot |= VKI_PROT_EXEC;
+   if (0)
+      VG_(printf)("init2: %p-%p prot %s stack=%d\n",
+		  start, start+size, VG_(prot_str)(prot), is_stack_segment);
 
    if (is_stack_segment)
       flags = SF_STACK | SF_GROWDOWN;
@@ -2294,22 +2229,51 @@
    if (filename != NULL)
       flags |= SF_FILE;
 
-   if (start >= VG_(valgrind_base) && (start+size-1) <= VG_(valgrind_last))
-      flags |= SF_VALGRIND;
+#if 0
+   // This needs to be fixed properly.   jrs 20050307
+   if (start >= VG_(client_end) && start < VG_(valgrind_last)) {
+      Segment *s = VG_(find_segment_before)(start);
 
-   VG_(map_file_segment)(start, size, prot, flags, dev, ino, foffset, filename);
+      /* We have to be a bit careful about inserting new mappings into
+	 the Valgrind part of the address space.  We're actively
+	 changing things as we parse these mappings, particularly in
+	 shadow memory, and so we don't want to overwrite those
+	 changes.  Therefore, we only insert/update a mapping if it is
+	 mapped from a file or it exactly matches an existing mapping.
 
-   if (VG_(is_client_addr)(start) && VG_(is_client_addr)(start+size-1))
-      VG_TRACK( new_mem_startup, start, size, rr=='r', ww=='w', xx=='x' );
+	 NOTE: we're only talking about the Segment list mapping
+	 metadata; this doesn't actually mmap anything more. */
+      if (filename || (s && s->addr == start && s->len == size)) {
+	 flags |= SF_VALGRIND;
+	 VG_(map_file_segment)(start, size, prot, flags, dev, ino, foffset, filename);
+      } else {
+	 /* assert range is already mapped */
+	 vg_assert(VG_(is_addressable)(start, size, VKI_PROT_NONE));
+      }
+   } else
+#endif
+      VG_(map_file_segment)(start, size, prot, flags, dev, ino, foffset, filename);
+
+   if (VG_(is_client_addr)(start) && VG_(is_client_addr)(start+size-1)) {
+	   VG_TRACK( new_mem_startup, start, size,
+		     !!(prot & VKI_PROT_READ), 
+                     !!(prot & VKI_PROT_WRITE), 
+                     !!(prot & VKI_PROT_EXEC));
+   }
 
    /* If this is the stack segment mark all below %esp as noaccess. */
    r_esp = sp_at_startup___global_arg;
    vg_assert(0 != r_esp);
    if (is_stack_segment) {
-      if (0)
-         VG_(message)(Vg_DebugMsg, "invalidating stack area: %x .. %x",
+      if (0) {
+         VG_(message)(Vg_DebugMsg, "invalidating stack area: %p .. %p",
                       start,r_esp);
+         VG_(message)(Vg_DebugMsg, "  validating stack area: %p .. %p",
+                      r_esp, start+size);
+      }
       VG_TRACK( die_mem_stack, start, r_esp-start );
+      // what's this for?
+      //VG_TRACK( post_mem_write, r_esp, (start+size)-r_esp );
    }
 }
 
@@ -2323,6 +2287,8 @@
 
 void VG_(sanity_check_general) ( Bool force_expensive )
 {
+   ThreadId tid;
+
    VGP_PUSHCC(VgpCoreCheapSanity);
 
    if (VG_(clo_sanity_level) < 1) return;
@@ -2351,8 +2317,6 @@
       VGP_PUSHCC(VgpCoreExpensiveSanity);
       sanity_slow_count++;
 
-      VG_(sanity_check_proxy)();
-
 #     if 0
       { void zzzmemscan(void); zzzmemscan(); }
 #     endif
@@ -2365,6 +2329,24 @@
           vg_assert(TL_(expensive_sanity_check)());
           VGP_POPCC(VgpToolExpensiveSanity);
       }
+
+      /* Check that Segments and /proc/self/maps match up */
+      //vg_assert(VG_(sanity_check_memory)());
+
+      /* Look for stack overruns.  Visit all threads. */
+      for(tid = 1; tid < VG_N_THREADS; tid++) {
+	 Int remains;
+
+	 if (VG_(threads)[tid].status == VgTs_Empty ||
+	     VG_(threads)[tid].status == VgTs_Zombie)
+	    continue;
+
+	 remains = VGA_(stack_unused)(tid);
+	 if (remains < VKI_PAGE_SIZE)
+	    VG_(message)(Vg_DebugMsg, "WARNING: Thread %d is within %d bytes of running out of stack!",
+			 tid, remains);
+      }
+
       /* 
       if ((sanity_fast_count % 500) == 0) VG_(mallocSanityCheckAll)(); 
       */
@@ -2441,10 +2423,10 @@
    return True;
 }
 
-int main(int argc, char **argv)
+int main(int argc, char **argv, char **envp)
 {
    char **cl_argv;
-   const char *tool = NULL;
+   const char *tool = "memcheck";   // default to Memcheck
    const char *exec = NULL;
    char *preload;          /* tool-specific LD_PRELOAD .so */
    char **env;
@@ -2455,13 +2437,8 @@
    Addr client_eip;
    Addr sp_at_startup;     /* client's SP at the point we gained control. */
    UInt * client_auxv;
-   VgSchedReturnCode src;
-   Int exitcode = 0;
-   Int fatal_sigNo = -1;
    struct vki_rlimit zero = { 0, 0 };
    Int padfile;
-   ThreadId last_run_tid = 0;    // Last thread the scheduler ran.
-
 
    //============================================================
    // Nb: startup is complex.  Prerequisites are shown at every step.
@@ -2472,8 +2449,8 @@
    //============================================================
    // Command line argument handling order:
    // * If --help/--help-debug are present, show usage message 
-   //   (if --tool is also present, that includes the tool-specific usage)
-   // * Then, if --tool is missing, abort with error msg
+   //   (including the tool-specific usage)
+   // * (If no --tool option given, default to Memcheck)
    // * Then, if client is missing, abort with error msg
    // * Then, if any cmdline args are bad, abort with error msg
    //============================================================
@@ -2497,7 +2474,6 @@
       void* init_sp = argv - 1;
       padfile = scan_auxv(init_sp);
    }
-
    if (0) {
       printf("========== main() ==========\n");
       foreach_map(prmap, /*dummy*/NULL);
@@ -2563,7 +2539,7 @@
    //   p: set-libdir  [for VG_(libdir)]
    //   p: load_tool() [for 'preload']
    //--------------------------------------------------------------
-   env = fix_environment(environ, preload);
+   env = fix_environment(envp, preload);
 
    //--------------------------------------------------------------
    // Setup client stack, eip, and VG_(client_arg[cv])
@@ -2572,8 +2548,10 @@
    //--------------------------------------------------------------
    { 
       void* init_sp = argv - 1;
+
       sp_at_startup = setup_client_stack(init_sp, cl_argv, env, &info,
                                          &client_auxv);
+      free(env);
    }
 
    if (0)
@@ -2587,27 +2565,13 @@
    //==============================================================
 
    //--------------------------------------------------------------
-   // atfork
-   //   p: n/a
-   //--------------------------------------------------------------
-   VG_(atfork)(NULL, NULL, newpid);
-   newpid(VG_INVALID_THREADID);
-
-   //--------------------------------------------------------------
    // setup file descriptors
    //   p: n/a
    //--------------------------------------------------------------
    setup_file_descriptors();
 
    //--------------------------------------------------------------
-   // Read /proc/self/maps into a buffer
-   //   p: all memory layout, environment setup   [so memory maps are right]
-   //--------------------------------------------------------------
-   VG_(read_procselfmaps)();
-
-   //--------------------------------------------------------------
    // Build segment map (Valgrind segments only)
-   //   p: read proc/self/maps
    //   p: tl_pre_clo_init()  [to setup new_mem_startup tracker]
    //--------------------------------------------------------------
    VG_(parse_procselfmaps) ( build_valgrind_map_callback );
@@ -2662,6 +2626,7 @@
 
    //--------------------------------------------------------------
    // Build segment map (all segments)
+   //   p: shadow/redzone segments
    //   p: setup_client_stack()  [for 'sp_at_startup']
    //   p: init tool             [for 'new_mem_startup']
    //--------------------------------------------------------------
@@ -2674,10 +2639,18 @@
    // Protect client trampoline page (which is also sysinfo stuff)
    //   p: segment stuff   [otherwise get seg faults...]
    //--------------------------------------------------------------
-   VG_(mprotect)( (void *)VG_(client_trampoline_code),
-                 VG_(trampoline_code_length), VKI_PROT_READ|VKI_PROT_EXEC );
+   {
+      Segment *seg;
+      VG_(mprotect)( (void *)VG_(client_trampoline_code),
+		     VG_(trampoline_code_length), VKI_PROT_READ|VKI_PROT_EXEC );
 #endif
 
+      /* Make sure this segment isn't treated as stack */
+      seg = VG_(find_segment)(VG_(client_trampoline_code));
+      if (seg)
+	 seg->flags &= ~(SF_STACK | SF_GROWDOWN);
+   }
+
    //==============================================================
    // Can use VG_(map)() after segments set up
    //==============================================================
@@ -2698,7 +2671,6 @@
       { Long q; for (q = 0; q < 10ULL *1000*1000*1000; q++) ; }
    }
 
-   //--------------------------------------------------------------
    // Search for file descriptors that are inherited from our parent
    //   p: process_cmd_line_options  [for VG_(clo_track_fds)]
    //--------------------------------------------------------------
@@ -2712,8 +2684,8 @@
    VG_(scheduler_init)();
 
    //--------------------------------------------------------------
-   // Set up state of thread 1
-   //   p: {pre,post}_clo_init()  [for tool helper registration]
+   // Initialise the pthread model
+   //   p: ?
    //      load_client()          [for 'client_eip']
    //      setup_client_stack()   [for 'sp_at_startup']
    //      setup_scheduler()      [for the rest of state 1 stuff]
@@ -2728,15 +2700,13 @@
    VG_(instr_ptr_offset) = offsetof(VexGuestArchState, ARCH_INSTR_PTR);
 
    //--------------------------------------------------------------
-   // Set up the ProxyLWP machinery
-   //   p: VG_(scheduler_init)()?  [XXX: subtle dependency?]
    //--------------------------------------------------------------
-   VG_(proxy_init)();
+   //if (VG_(clo_model_pthreads))
+   //   VG_(pthread_init)();
 
    //--------------------------------------------------------------
    // Initialise the signal handling subsystem
-   //   p: VG_(atfork)(NULL, NULL, newpid) [else problems with sigmasks]
-   //   p: VG_(proxy_init)()               [else breaks...]
+   //   p: n/a
    //--------------------------------------------------------------
    // Nb: temporarily parks the saved blocking-mask in saved_sigmask.
    VG_(sigstartup_actions)();
@@ -2774,13 +2744,6 @@
    VG_(init_tt_tc)();
 
    //--------------------------------------------------------------
-   // Read debug info to find glibc entry points to intercept
-   //   p: parse_procselfmaps? [XXX for debug info?]
-   //   p: init_tt_tc?  [XXX ???]
-   //--------------------------------------------------------------
-   VG_(setup_code_redirect_table)();
-
-   //--------------------------------------------------------------
    // Verbosity message
    //   p: end_rdtsc_calibration [so startup message is printed first]
    //--------------------------------------------------------------
@@ -2800,12 +2763,36 @@
    // Run!
    //--------------------------------------------------------------
    VGP_POPCC(VgpStartup);
-   VGP_PUSHCC(VgpSched);
 
-   src = VG_(scheduler)( &exitcode, &last_run_tid, &fatal_sigNo );
+   vg_assert(VG_(master_tid) == 1);
 
-   VGP_POPCC(VgpSched);
+   VGA_(main_thread_wrapper)(1);
 
+   abort();
+}
+
+
+/* Do everything which needs doing when the last thread exits */
+void VG_(shutdown_actions)(ThreadId tid)
+{
+   vg_assert(tid == VG_(master_tid));
+   vg_assert(VG_(is_running_thread)(tid));
+
+   // Wait for all other threads to exit.
+   VGA_(reap_threads)(tid);
+
+   VG_(clo_model_pthreads) = False;
+
+   // Clean the client up before the final report
+   VGA_(final_tidyup)(tid);
+
+   // OK, done
+   VG_(exit_thread)(tid);
+
+   /* should be no threads left */
+   vg_assert(VG_(count_living_threads)() == 0);
+
+   VG_(threads)[tid].status = VgTs_Empty;
    //--------------------------------------------------------------
    // Finalisation: cleanup, messages, etc.  Order no so important, only
    // affects what order the messages come.
@@ -2813,11 +2800,6 @@
    if (VG_(clo_verbosity) > 0)
       VG_(message)(Vg_UserMsg, "");
 
-   if (src == VgSrc_Deadlock) {
-     VG_(message)(Vg_UserMsg, 
-        "Warning: pthread scheduler exited due to deadlock");
-   }
-
    /* Print out file descriptor summary and stats. */
    if (VG_(clo_track_fds))
       VG_(show_open_fds)();
@@ -2825,7 +2807,7 @@
    if (VG_(needs).core_errors || VG_(needs).tool_errors)
       VG_(show_all_errors)();
 
-   TL_(fini)( exitcode );
+   TL_(fini)( 0 /*exitcode*/ );
 
    VG_(sanity_check_general)( True /*include expensive checks*/ );
 
@@ -2834,58 +2816,15 @@
 
    if (VG_(clo_profile))
       VGP_(done_profiling)();
-
    if (VG_(clo_profile_flags) > 0)
       VG_(show_BB_profile)();
 
-   /* We're exiting, so nuke all the threads and clean up the proxy LWPs */
-   vg_assert(src == VgSrc_FatalSig ||
-	     VG_(threads)[last_run_tid].status == VgTs_Runnable ||
-	     VG_(threads)[last_run_tid].status == VgTs_WaitJoiner);
-   VG_(nuke_all_threads_except)(VG_INVALID_THREADID);
-
    /* Print Vex storage stats */
    if (0)
        LibVEX_ShowAllocStats();
 
-   //--------------------------------------------------------------
-   // Exit, according to the scheduler's return code
-   //--------------------------------------------------------------
-   switch (src) {
-      case VgSrc_ExitSyscall: /* the normal way out */
-         vg_assert(last_run_tid > 0 && last_run_tid < VG_N_THREADS);
-	 VG_(proxy_shutdown)();
-
-         /* The thread's %EBX at the time it did __NR_exit() will hold
-            the arg to __NR_exit(), so we just do __NR_exit() with
-            that arg. */
-         VG_(exit)( exitcode );
-         /* NOT ALIVE HERE! */
-         VG_(core_panic)("entered the afterlife in main() -- ExitSyscall");
-         break; /* what the hell :) */
-
-      case VgSrc_Deadlock:
-         /* Just exit now.  No point in continuing. */
-	 VG_(proxy_shutdown)();
-         VG_(exit)(0);
-         VG_(core_panic)("entered the afterlife in main() -- Deadlock");
-         break;
-
-      case VgSrc_FatalSig:
-	 /* We were killed by a fatal signal, so replicate the effect */
-	 vg_assert(fatal_sigNo != -1);
-	 VG_(kill_self)(fatal_sigNo);
-	 VG_(core_panic)("main(): signal was supposed to be fatal");
-	 break;
-
-      default:
-         VG_(core_panic)("main(): unexpected scheduler return code");
-   }
-
-   abort();
 }
 
-
 /*--------------------------------------------------------------------*/
 /*--- end                                                vg_main.c ---*/
 /*--------------------------------------------------------------------*/