Common up leak detection stuff which was previously duplicated in Memcheck
and Addrcheck.  In coregrind/vg_memory.c, create

   void VG_(generic_detect_memory_leaks

and remove several hundred lines of code from both ac_main.c and mc_main.c.


git-svn-id: svn://svn.valgrind.org/valgrind/trunk@1250 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/memcheck/mc_main.c b/memcheck/mc_main.c
index dcde8dc..b8c0126 100644
--- a/memcheck/mc_main.c
+++ b/memcheck/mc_main.c
@@ -37,9 +37,6 @@
 /* Define to debug the mem audit system. */
 /* #define VG_DEBUG_MEMORY */
 
-/* Define to debug the memory-leak-detector. */
-/* #define VG_DEBUG_LEAKCHECK */
-
 /* Define to collect detailed performance info. */
 /* #define VG_PROFILE_MEMORY */
 
@@ -1630,476 +1627,58 @@
    add_to_freed_queue ( sc );
 }
 
-/*------------------------------------------------------------*/
-/*--- Low-level address-space scanning, for the leak       ---*/
-/*--- detector.                                            ---*/
-/*------------------------------------------------------------*/
-
-static 
-jmp_buf memscan_jmpbuf;
-
-static
-void vg_scan_all_valid_memory_sighandler ( Int sigNo )
-{
-   __builtin_longjmp(memscan_jmpbuf, 1);
-}
-
-/* Safely (avoiding SIGSEGV / SIGBUS) scan the entire valid address
-   space and pass the addresses and values of all addressible,
-   defined, aligned words to notify_word.  This is the basis for the
-   leak detector.  Returns the number of calls made to notify_word.  */
-UInt VG_(scan_all_valid_memory) ( void (*notify_word)( Addr, UInt ) )
-{
-   /* All volatile, because some gccs seem paranoid about longjmp(). */
-   volatile UInt res, numPages, page, vbytes, primaryMapNo, nWordsNotified;
-   volatile Addr pageBase, addr;
-   volatile SecMap* sm;
-   volatile UChar abits;
-   volatile UInt page_first_word;
-
-   vki_ksigaction sigbus_saved;
-   vki_ksigaction sigbus_new;
-   vki_ksigaction sigsegv_saved;
-   vki_ksigaction sigsegv_new;
-   vki_ksigset_t  blockmask_saved;
-   vki_ksigset_t  unblockmask_new;
-
-   /* Temporarily install a new sigsegv and sigbus handler, and make
-      sure SIGBUS, SIGSEGV and SIGTERM are unblocked.  (Perhaps the
-      first two can never be blocked anyway?)  */
-
-   sigbus_new.ksa_handler = vg_scan_all_valid_memory_sighandler;
-   sigbus_new.ksa_flags = VKI_SA_ONSTACK | VKI_SA_RESTART;
-   sigbus_new.ksa_restorer = NULL;
-   res = VG_(ksigemptyset)( &sigbus_new.ksa_mask );
-   sk_assert(res == 0);
-
-   sigsegv_new.ksa_handler = vg_scan_all_valid_memory_sighandler;
-   sigsegv_new.ksa_flags = VKI_SA_ONSTACK | VKI_SA_RESTART;
-   sigsegv_new.ksa_restorer = NULL;
-   res = VG_(ksigemptyset)( &sigsegv_new.ksa_mask );
-   sk_assert(res == 0+0);
-
-   res =  VG_(ksigemptyset)( &unblockmask_new );
-   res |= VG_(ksigaddset)( &unblockmask_new, VKI_SIGBUS );
-   res |= VG_(ksigaddset)( &unblockmask_new, VKI_SIGSEGV );
-   res |= VG_(ksigaddset)( &unblockmask_new, VKI_SIGTERM );
-   sk_assert(res == 0+0+0);
-
-   res = VG_(ksigaction)( VKI_SIGBUS, &sigbus_new, &sigbus_saved );
-   sk_assert(res == 0+0+0+0);
-
-   res = VG_(ksigaction)( VKI_SIGSEGV, &sigsegv_new, &sigsegv_saved );
-   sk_assert(res == 0+0+0+0+0);
-
-   res = VG_(ksigprocmask)( VKI_SIG_UNBLOCK, &unblockmask_new, &blockmask_saved );
-   sk_assert(res == 0+0+0+0+0+0);
-
-   /* The signal handlers are installed.  Actually do the memory scan. */
-   numPages = 1 << (32-VKI_BYTES_PER_PAGE_BITS);
-   sk_assert(numPages == 1048576);
-   sk_assert(4096 == (1 << VKI_BYTES_PER_PAGE_BITS));
-
-   nWordsNotified = 0;
-
-   for (page = 0; page < numPages; page++) {
-      pageBase = page << VKI_BYTES_PER_PAGE_BITS;
-      primaryMapNo = pageBase >> 16;
-      sm = primary_map[primaryMapNo];
-      if (IS_DISTINGUISHED_SM(sm)) continue;
-      if (__builtin_setjmp(memscan_jmpbuf) == 0) {
-         /* try this ... */
-         page_first_word = * (volatile UInt*)pageBase;
-         /* we get here if we didn't get a fault */
-         /* Scan the page */
-         for (addr = pageBase; addr < pageBase+VKI_BYTES_PER_PAGE; addr += 4) {
-            abits  = get_abits4_ALIGNED(addr);
-            vbytes = get_vbytes4_ALIGNED(addr);
-            if (abits == VGM_NIBBLE_VALID 
-                && vbytes == VGM_WORD_VALID) {
-               nWordsNotified++;
-               notify_word ( addr, *(UInt*)addr );
-	    }
-         }
-      } else {
-         /* We get here if reading the first word of the page caused a
-            fault, which in turn caused the signal handler to longjmp.
-            Ignore this page. */
-         if (0)
-         VG_(printf)(
-            "vg_scan_all_valid_memory_sighandler: ignoring page at %p\n",
-            (void*)pageBase 
-         );
-      }
-   }
-
-   /* Restore signal state to whatever it was before. */
-   res = VG_(ksigaction)( VKI_SIGBUS, &sigbus_saved, NULL );
-   sk_assert(res == 0 +0);
-
-   res = VG_(ksigaction)( VKI_SIGSEGV, &sigsegv_saved, NULL );
-   sk_assert(res == 0 +0 +0);
-
-   res = VG_(ksigprocmask)( VKI_SIG_SETMASK, &blockmask_saved, NULL );
-   sk_assert(res == 0 +0 +0 +0);
-
-   return nWordsNotified;
-}
-
 
 /*------------------------------------------------------------*/
 /*--- Detecting leaked (unreachable) malloc'd blocks.      ---*/
 /*------------------------------------------------------------*/
 
-/* A block is either 
-   -- Proper-ly reached; a pointer to its start has been found
-   -- Interior-ly reached; only an interior pointer to it has been found
-   -- Unreached; so far, no pointers to any part of it have been found. 
+/* For the memory leak detector, say whether an entire 64k chunk of
+   address space is possibly in use, or not.  If in doubt return
+   True.
 */
-typedef 
-   enum { Unreached, Interior, Proper } 
-   Reachedness;
-
-/* A block record, used for generating err msgs. */
-typedef
-   struct _LossRecord {
-      struct _LossRecord* next;
-      /* Where these lost blocks were allocated. */
-      ExeContext*  allocated_at;
-      /* Their reachability. */
-      Reachedness  loss_mode;
-      /* Number of blocks and total # bytes involved. */
-      UInt         total_bytes;
-      UInt         num_blocks;
-   }
-   LossRecord;
-
-
-/* Find the i such that ptr points at or inside the block described by
-   shadows[i].  Return -1 if none found.  This assumes that shadows[]
-   has been sorted on the ->data field. */
-
-#ifdef VG_DEBUG_LEAKCHECK
-/* Used to sanity-check the fast binary-search mechanism. */
-static Int find_shadow_for_OLD ( Addr          ptr, 
-                                 ShadowChunk** shadows,
-                                 Int           n_shadows )
-
+static
+Bool mc_is_valid_64k_chunk ( UInt chunk_number )
 {
-   Int  i;
-   Addr a_lo, a_hi;
-   PROF_EVENT(70);
-   for (i = 0; i < n_shadows; i++) {
-      PROF_EVENT(71);
-      a_lo = shadows[i]->data;
-      a_hi = ((Addr)shadows[i]->data) + shadows[i]->size - 1;
-      if (a_lo <= ptr && ptr <= a_hi)
-         return i;
-   }
-   return -1;
-}
-#endif
-
-
-static Int find_shadow_for ( Addr          ptr, 
-                             ShadowChunk** shadows,
-                             Int           n_shadows )
-{
-   Addr a_mid_lo, a_mid_hi;
-   Int lo, mid, hi, retVal;
-   PROF_EVENT(70);
-   /* VG_(printf)("find shadow for %p = ", ptr); */
-   retVal = -1;
-   lo = 0;
-   hi = n_shadows-1;
-   while (True) {
-      PROF_EVENT(71);
-
-      /* invariant: current unsearched space is from lo to hi,
-         inclusive. */
-      if (lo > hi) break; /* not found */
-
-      mid      = (lo + hi) / 2;
-      a_mid_lo = shadows[mid]->data;
-      a_mid_hi = ((Addr)shadows[mid]->data) + shadows[mid]->size - 1;
-
-      if (ptr < a_mid_lo) {
-         hi = mid-1;
-         continue;
-      } 
-      if (ptr > a_mid_hi) {
-         lo = mid+1;
-         continue;
-      }
-      sk_assert(ptr >= a_mid_lo && ptr <= a_mid_hi);
-      retVal = mid;
-      break;
-   }
-
-#  ifdef VG_DEBUG_LEAKCHECK
-   sk_assert(retVal == find_shadow_for_OLD ( ptr, shadows, n_shadows ));
-#  endif
-   /* VG_(printf)("%d\n", retVal); */
-   return retVal;
-}
-
-
-
-static void sort_malloc_shadows ( ShadowChunk** shadows, UInt n_shadows )
-{
-   Int   incs[14] = { 1, 4, 13, 40, 121, 364, 1093, 3280,
-                      9841, 29524, 88573, 265720,
-                      797161, 2391484 };
-   Int          lo = 0;
-   Int          hi = n_shadows-1;
-   Int          i, j, h, bigN, hp;
-   ShadowChunk* v;
-
-   PROF_EVENT(72);
-   bigN = hi - lo + 1; if (bigN < 2) return;
-   hp = 0; while (incs[hp] < bigN) hp++; hp--;
-
-   for (; hp >= 0; hp--) {
-      PROF_EVENT(73);
-      h = incs[hp];
-      i = lo + h;
-      while (1) {
-         PROF_EVENT(74);
-         if (i > hi) break;
-         v = shadows[i];
-         j = i;
-         while (shadows[j-h]->data > v->data) {
-            PROF_EVENT(75);
-            shadows[j] = shadows[j-h];
-            j = j - h;
-            if (j <= (lo + h - 1)) break;
-         }
-         shadows[j] = v;
-         i++;
-      }
-   }
-}
-
-/* Globals, for the callback used by SK_(detect_memory_leaks). */
-
-static ShadowChunk** vglc_shadows;
-static Int           vglc_n_shadows;
-static Reachedness*  vglc_reachedness;
-static Addr          vglc_min_mallocd_addr;
-static Addr          vglc_max_mallocd_addr;
-
-static 
-void vg_detect_memory_leaks_notify_addr ( Addr a, UInt word_at_a )
-{
-   Int  sh_no;
-   Addr ptr;
-
-   /* Rule out some known causes of bogus pointers.  Mostly these do
-      not cause much trouble because only a few false pointers can
-      ever lurk in these places.  This mainly stops it reporting that
-      blocks are still reachable in stupid test programs like this
-
-         int main (void) { char* a = malloc(100); return 0; }
-
-      which people seem inordinately fond of writing, for some reason.  
-
-      Note that this is a complete kludge.  It would be better to
-      ignore any addresses corresponding to valgrind.so's .bss and
-      .data segments, but I cannot think of a reliable way to identify
-      where the .bss segment has been put.  If you can, drop me a
-      line.  
-   */
-   if (VG_(within_stack)(a))                return;
-   if (VG_(within_m_state_static)(a))       return;
-   if (a == (Addr)(&vglc_min_mallocd_addr)) return;
-   if (a == (Addr)(&vglc_max_mallocd_addr)) return;
-
-   /* OK, let's get on and do something Useful for a change. */
-
-   ptr = (Addr)word_at_a;
-   if (ptr >= vglc_min_mallocd_addr && ptr <= vglc_max_mallocd_addr) {
-      /* Might be legitimate; we'll have to investigate further. */
-      sh_no = find_shadow_for ( ptr, vglc_shadows, vglc_n_shadows );
-      if (sh_no != -1) {
-         /* Found a block at/into which ptr points. */
-         sk_assert(sh_no >= 0 && sh_no < vglc_n_shadows);
-         sk_assert(ptr < vglc_shadows[sh_no]->data 
-                         + vglc_shadows[sh_no]->size);
-         /* Decide whether Proper-ly or Interior-ly reached. */
-         if (ptr == vglc_shadows[sh_no]->data) {
-            if (0) VG_(printf)("pointer at %p to %p\n", a, word_at_a );
-            vglc_reachedness[sh_no] = Proper;
-         } else {
-            if (vglc_reachedness[sh_no] == Unreached)
-               vglc_reachedness[sh_no] = Interior;
-         }
-      }
+   sk_assert(chunk_number >= 0 && chunk_number < 65536);
+   if (IS_DISTINGUISHED_SM(primary_map[chunk_number])) {
+      /* Definitely not in use. */
+      return False;
+   } else {
+      return True;
    }
 }
 
 
+/* For the memory leak detector, say whether or not a given word
+   address is to be regarded as valid. */
+static
+Bool mc_is_valid_address ( Addr a )
+{
+   UInt vbytes;
+   UChar abits;
+   sk_assert(IS_ALIGNED4_ADDR(a));
+   abits  = get_abits4_ALIGNED(a);
+   vbytes = get_vbytes4_ALIGNED(a);
+   if (abits == VGM_NIBBLE_VALID && vbytes == VGM_WORD_VALID) {
+      return True;
+   } else {
+      return False;
+   }
+}
+
+
+/* Leak detector for this skin.  We don't actually do anything, merely
+   run the generic leak detector with suitable parameters for this
+   skin. */
 void SK_(detect_memory_leaks) ( void )
 {
-   Int    i;
-   Int    blocks_leaked, bytes_leaked;
-   Int    blocks_dubious, bytes_dubious;
-   Int    blocks_reachable, bytes_reachable;
-   Int    n_lossrecords;
-   UInt   bytes_notified;
-   
-   LossRecord*  errlist;
-   LossRecord*  p;
-
-   PROF_EVENT(76);
-
-   /* VG_(get_malloc_shadows) allocates storage for shadows */
-   vglc_shadows = VG_(get_malloc_shadows)( &vglc_n_shadows );
-   if (vglc_n_shadows == 0) {
-      sk_assert(vglc_shadows == NULL);
-      VG_(message)(Vg_UserMsg, 
-                   "No malloc'd blocks -- no leaks are possible.\n");
-      return;
-   }
-
-   VG_(message)(Vg_UserMsg, 
-                "searching for pointers to %d not-freed blocks.", 
-                vglc_n_shadows );
-   sort_malloc_shadows ( vglc_shadows, vglc_n_shadows );
-
-   /* Sanity check; assert that the blocks are now in order and that
-      they don't overlap. */
-   for (i = 0; i < vglc_n_shadows-1; i++) {
-      sk_assert( ((Addr)vglc_shadows[i]->data)
-                 < ((Addr)vglc_shadows[i+1]->data) );
-      sk_assert( ((Addr)vglc_shadows[i]->data) + vglc_shadows[i]->size
-                 < ((Addr)vglc_shadows[i+1]->data) );
-   }
-
-   vglc_min_mallocd_addr = ((Addr)vglc_shadows[0]->data);
-   vglc_max_mallocd_addr = ((Addr)vglc_shadows[vglc_n_shadows-1]->data)
-                         + vglc_shadows[vglc_n_shadows-1]->size - 1;
-
-   vglc_reachedness 
-      = VG_(malloc)( vglc_n_shadows * sizeof(Reachedness) );
-   for (i = 0; i < vglc_n_shadows; i++)
-      vglc_reachedness[i] = Unreached;
-
-   /* Do the scan of memory. */
-   bytes_notified
-       = VG_(scan_all_valid_memory)( &vg_detect_memory_leaks_notify_addr )
-         * VKI_BYTES_PER_WORD;
-
-   VG_(message)(Vg_UserMsg, "checked %d bytes.", bytes_notified);
-
-   blocks_leaked    = bytes_leaked    = 0;
-   blocks_dubious   = bytes_dubious   = 0;
-   blocks_reachable = bytes_reachable = 0;
-
-   for (i = 0; i < vglc_n_shadows; i++) {
-      if (vglc_reachedness[i] == Unreached) {
-         blocks_leaked++;
-         bytes_leaked += vglc_shadows[i]->size;
-      }
-      else if (vglc_reachedness[i] == Interior) {
-         blocks_dubious++;
-         bytes_dubious += vglc_shadows[i]->size;
-      }
-      else if (vglc_reachedness[i] == Proper) {
-         blocks_reachable++;
-         bytes_reachable += vglc_shadows[i]->size;
-      }
-   }
-
-   VG_(message)(Vg_UserMsg, "");
-   VG_(message)(Vg_UserMsg, "definitely lost: %d bytes in %d blocks.", 
-                            bytes_leaked, blocks_leaked );
-   VG_(message)(Vg_UserMsg, "possibly lost:   %d bytes in %d blocks.", 
-                            bytes_dubious, blocks_dubious );
-   VG_(message)(Vg_UserMsg, "still reachable: %d bytes in %d blocks.", 
-                            bytes_reachable, blocks_reachable );
-
-
-   /* Common up the lost blocks so we can print sensible error
-      messages. */
-
-   n_lossrecords = 0;
-   errlist       = NULL;
-   for (i = 0; i < vglc_n_shadows; i++) {
-     
-      /* 'where' stored in 'skin_extra' field */
-      ExeContext* where = get_where ( vglc_shadows[i] );
-
-      for (p = errlist; p != NULL; p = p->next) {
-         if (p->loss_mode == vglc_reachedness[i]
-             && VG_(eq_ExeContext) ( SK_(clo_leak_resolution),
-                                     p->allocated_at, 
-                                     where) ) {
-            break;
-	 }
-      }
-      if (p != NULL) {
-         p->num_blocks  ++;
-         p->total_bytes += vglc_shadows[i]->size;
-      } else {
-         n_lossrecords ++;
-         p = VG_(malloc)(sizeof(LossRecord));
-         p->loss_mode    = vglc_reachedness[i];
-         p->allocated_at = where;
-         p->total_bytes  = vglc_shadows[i]->size;
-         p->num_blocks   = 1;
-         p->next         = errlist;
-         errlist         = p;
-      }
-   }
-   
-   for (i = 0; i < n_lossrecords; i++) {
-      LossRecord* p_min = NULL;
-      UInt        n_min = 0xFFFFFFFF;
-      for (p = errlist; p != NULL; p = p->next) {
-         if (p->num_blocks > 0 && p->total_bytes < n_min) {
-            n_min = p->total_bytes;
-            p_min = p;
-         }
-      }
-      sk_assert(p_min != NULL);
-
-      if ( (!SK_(clo_show_reachable)) && p_min->loss_mode == Proper) {
-         p_min->num_blocks = 0;
-         continue;
-      }
-
-      VG_(message)(Vg_UserMsg, "");
-      VG_(message)(
-         Vg_UserMsg,
-         "%d bytes in %d blocks are %s in loss record %d of %d",
-         p_min->total_bytes, p_min->num_blocks,
-         p_min->loss_mode==Unreached ? "definitely lost" :
-            (p_min->loss_mode==Interior ? "possibly lost"
-                                        : "still reachable"),
-         i+1, n_lossrecords
-      );
-      VG_(pp_ExeContext)(p_min->allocated_at);
-      p_min->num_blocks = 0;
-   }
-
-   VG_(message)(Vg_UserMsg, "");
-   VG_(message)(Vg_UserMsg, "LEAK SUMMARY:");
-   VG_(message)(Vg_UserMsg, "   definitely lost: %d bytes in %d blocks.", 
-                            bytes_leaked, blocks_leaked );
-   VG_(message)(Vg_UserMsg, "   possibly lost:   %d bytes in %d blocks.", 
-                            bytes_dubious, blocks_dubious );
-   VG_(message)(Vg_UserMsg, "   still reachable: %d bytes in %d blocks.", 
-                            bytes_reachable, blocks_reachable );
-   if (!SK_(clo_show_reachable)) {
-      VG_(message)(Vg_UserMsg, 
-         "Reachable blocks (those to which a pointer was found) are not shown.");
-      VG_(message)(Vg_UserMsg, 
-         "To see them, rerun with: --show-reachable=yes");
-   }
-   VG_(message)(Vg_UserMsg, "");
-
-   VG_(free) ( vglc_shadows );
-   VG_(free) ( vglc_reachedness );
+   VG_(generic_detect_memory_leaks) ( 
+      mc_is_valid_64k_chunk,
+      mc_is_valid_address,
+      get_where,
+      SK_(clo_leak_resolution),
+      SK_(clo_show_reachable)
+   );
 }