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/addrcheck/ac_main.c b/addrcheck/ac_main.c
index 7075061..10e2daa 100644
--- a/addrcheck/ac_main.c
+++ b/addrcheck/ac_main.c
@@ -1798,475 +1798,55 @@
}
-
-/*------------------------------------------------------------*/
-/*--- 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, primaryMapNo, nWordsNotified;
- volatile Addr pageBase, addr;
- volatile AcSecMap* 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);
- if (abits == VGM_NIBBLE_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 ac_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 ac_is_valid_address ( Addr a )
+{
+ UChar abits;
+ sk_assert(IS_ALIGNED4_ADDR(a));
+ abits = get_abits4_ALIGNED(a);
+ if (abits == VGM_NIBBLE_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) (
+ ac_is_valid_64k_chunk,
+ ac_is_valid_address,
+ get_where,
+ SK_(clo_leak_resolution),
+ SK_(clo_show_reachable)
+ );
}