Fix leak scan SEGV catcher when ptr starts in unreadable page (readable for aspacemgr)

The fault catcher installed during leak scan to catter e.g. for
possible desynchronisation between real protection and aspacemgr
was not activated when the scanned ptr was directly pointing in
a desynchronised page.
This was (initially only) visible on ppc32 (gcc110) as the page size of
gcc110 is big (64 K).

=> modified the leak-segv-jmp test so as to produce the problem also 
on systems with smaller pages.

The fix consists in calling the setjmp before the scan loop,
and skip the bad address which has been recorded by the fault
catcher.
Also, deemed better to just skip one single Addr rather than a full page
(e.g. to skip less data in case some addresses are unreadable e.g.
on strange hardware).

Performance of the leak scan has been measured, seems slightly
faster on x86,amd64 and ppc32. Slightly slower on ppc64.

Also if verbose argument is given, outputs the nr of bytes skipped
due to fault.



git-svn-id: svn://svn.valgrind.org/valgrind/trunk@13623 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/memcheck/mc_leakcheck.c b/memcheck/mc_leakcheck.c
index 4555a22..ffbf650 100644
--- a/memcheck/mc_leakcheck.c
+++ b/memcheck/mc_leakcheck.c
@@ -488,6 +488,9 @@
 // Keeps track of how many bytes of memory we've scanned, for printing.
 // (Nb: We don't keep track of how many register bytes we've scanned.)
 static SizeT lc_scanned_szB;
+// Keeps track of how many bytes we have not scanned due to read errors that
+// caused a signal such as SIGSEGV.
+static SizeT lc_sig_skipped_szB;
 
 
 SizeT MC_(bytes_leaked)     = 0;
@@ -886,14 +889,17 @@
 
 
 static VG_MINIMAL_JMP_BUF(memscan_jmpbuf);
+static volatile Addr bad_scanned_addr;
 
 static
 void scan_all_valid_memory_catcher ( Int sigNo, Addr addr )
 {
    if (0)
       VG_(printf)("OUCH! sig=%d addr=%#lx\n", sigNo, addr);
-   if (sigNo == VKI_SIGSEGV || sigNo == VKI_SIGBUS)
+   if (sigNo == VKI_SIGSEGV || sigNo == VKI_SIGBUS) {
+      bad_scanned_addr = addr;
       VG_MINIMAL_LONGJMP(memscan_jmpbuf);
+   }
 }
 
 // lc_scan_memory has 2 modes:
@@ -935,8 +941,8 @@
       end portions of the block if they are not aligned on sizeof(Addr):
       These cannot be a valid pointer, and calls to MC_(is_valid_aligned_word)
       will assert for a non aligned address. */
-   Addr ptr = VG_ROUNDUP(start,     sizeof(Addr));
-   Addr end = VG_ROUNDDN(start+len, sizeof(Addr));
+   Addr ptr = VG_ROUNDUP(start, sizeof(Addr));
+   const Addr end = VG_ROUNDDN(start+len, sizeof(Addr));
    vki_sigset_t sigmask;
 
    if (VG_DEBUG_LEAKCHECK)
@@ -966,10 +972,28 @@
       // if not, skip onto the next page.
       ptr = VG_PGROUNDUP(ptr+1);        // First page is bad - skip it.
    }
-   /* This optimisation and below loop is based on some relationships between
-      VKI_PAGE_SIZE, SM_SIZE and sizeof(Addr) which are asserted in
+   /* The above optimisation and below loop is based on some relationships
+      between VKI_PAGE_SIZE, SM_SIZE and sizeof(Addr) which are asserted in
       MC_(detect_memory_leaks). */
 
+   // During scan, we check with aspacemgr that each page is readable and
+   // belongs to client.
+   // We still protect against SIGSEGV and SIGBUS e.g. in case aspacemgr is
+   // desynchronised with the real page mappings.
+   // Such a desynchronisation could happen due to an aspacemgr bug.
+   // Note that if the application is using mprotect(NONE), then
+   // a page can be unreadable but have addressable and defined
+   // VA bits (see mc_main.c function mc_new_mem_mprotect).
+   if (VG_MINIMAL_SETJMP(memscan_jmpbuf) != 0) {
+      // Catch read error ...
+      // We need to restore the signal mask, because we were
+      // longjmped out of a signal handler.
+      VG_(sigprocmask)(VKI_SIG_SETMASK, &sigmask, NULL);
+      lc_sig_skipped_szB += sizeof(Addr);
+      tl_assert(bad_scanned_addr >= VG_ROUNDUP(start, sizeof(Addr)));
+      tl_assert(bad_scanned_addr < VG_ROUNDDN(start+len, sizeof(Addr)));
+      ptr = bad_scanned_addr + sizeof(Addr); // Unaddressable, - skip it.
+   }
    while (ptr < end) {
       Addr addr;
 
@@ -987,29 +1011,11 @@
             ptr += VKI_PAGE_SIZE;      // Bad page - skip it.
             continue;
          }
-         // aspacemgr indicates the page is readable and belongs to client.
-         // We still probe the page explicitely in case aspacemgr is
-         // desynchronised with the real page mappings.
-         // Such a desynchronisation can happen due to an aspacemgr bug.
-         // Note that if the application is using mprotect(NONE), then
-         // a page can be unreadable but have addressable and defined
-         // VA bits (see mc_main.c function mc_new_mem_mprotect).
-         if (VG_MINIMAL_SETJMP(memscan_jmpbuf) == 0) {
-            // Try a read in the beginning of the page ...
-            Addr test = *(volatile Addr *)ptr;
-            __asm__ __volatile__("": :"r"(test) : "cc","memory");
-         } else {
-            // Catch read error ...
-            // We need to restore the signal mask, because we were
-            // longjmped out of a signal handler.
-            VG_(sigprocmask)(VKI_SIG_SETMASK, &sigmask, NULL);
-            ptr += VKI_PAGE_SIZE;      // Bad page - skip it.
-            continue;
-         }
       }
 
       if ( MC_(is_valid_aligned_word)(ptr) ) {
          lc_scanned_szB += sizeof(Addr);
+         // If the below read fails, we will longjmp to the loop begin.
          addr = *(Addr *)ptr;
          // If we get here, the scanned word is in valid memory.  Now
          // let's see if its contents point to a chunk.
@@ -1034,10 +1040,10 @@
                                      ch->data, addr, pp_heuristic(h));
                         }
                      }
-                     // Verify the loop above has properly scanned all heuristics.
-                     // If the below fails, it probably means the LeakCheckHeuristic
-                     // enum is not in sync anymore with the above loop and/or
-                     // with N_LEAK_CHECK_HEURISTICS.
+                     // Verify the loop above has properly scanned all
+                     // heuristics. If the below fails, it probably means the
+                     // LeakCheckHeuristic enum is not in sync anymore with the
+                     // above loop and/or with N_LEAK_CHECK_HEURISTICS.
                      tl_assert (h == N_LEAK_CHECK_HEURISTICS);
                   }
                }
@@ -1556,6 +1562,7 @@
    tl_assert(seg_starts && n_seg_starts > 0);
 
    lc_scanned_szB = 0;
+   lc_sig_skipped_szB = 0;
 
    // VG_(am_show_nsegments)( 0, "leakcheck");
    for (i = 0; i < n_seg_starts; i++) {
@@ -1757,6 +1764,9 @@
 
    if (VG_(clo_verbosity) > 1 && !VG_(clo_xml)) {
       VG_(umsg)("Checked %'lu bytes\n", lc_scanned_szB);
+      if (lc_sig_skipped_szB > 0)
+         VG_(umsg)("Skipped %'lu bytes due to read errors\n",
+                   lc_sig_skipped_szB);
       VG_(umsg)( "\n" );
    }