From: Julian Seward Date: Mon, 21 Oct 2002 07:29:59 +0000 (+0000) Subject: Common up leak detection stuff which was previously duplicated in Memcheck X-Git-Tag: svn/VALGRIND_1_9_4~197 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=976b63337a010713f8e6097db1ec1208eaf954bc;p=thirdparty%2Fvalgrind.git 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 --- diff --git a/addrcheck/ac_main.c b/addrcheck/ac_main.c index 7075061dd4..10e2daad9b 100644 --- a/addrcheck/ac_main.c +++ b/addrcheck/ac_main.c @@ -1798,475 +1798,55 @@ UCodeBlock* SK_(instrument)(UCodeBlock* cb_in, Addr orig_addr) } - -/*------------------------------------------------------------*/ -/*--- 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 ) - -{ - 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 ) +static +Bool ac_is_valid_64k_chunk ( UInt chunk_number ) { - 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++; - } + 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; } } -/* 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 ) +/* 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 ) { - 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; - } - } + 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) + ); } diff --git a/coregrind/vg_memory.c b/coregrind/vg_memory.c index 86a40d5f16..d85bbf037e 100644 --- a/coregrind/vg_memory.c +++ b/coregrind/vg_memory.c @@ -32,6 +32,9 @@ #include "vg_include.h" +/* Define to debug the memory-leak-detector. */ +/* #define VG_DEBUG_LEAKCHECK */ + /*--------------------------------------------------------------*/ /*--- Initialise program data/text etc on program startup. ---*/ @@ -439,6 +442,517 @@ static void vg_handle_esp_assignment_SLOWLY ( Addr old_esp, Addr new_esp ) } +/*--------------------------------------------------------------------*/ +/*--- Support for memory leak detectors ---*/ +/*--------------------------------------------------------------------*/ + +/*------------------------------------------------------------*/ +/*--- 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. + + Addresses are validated 3 ways. First we enquire whether (addr >> + 16) denotes a 64k chunk in use, by asking is_valid_64k_chunk(). If + so, we decide for ourselves whether each x86-level (4 K) page in + the chunk is safe to inspect. If yes, we enquire with + is_valid_address() whether or not each of the 1024 word-locations + on the page is valid. Only if so are that address and its contents + passed to notify_word. + + This is all to avoid duplication of this machinery between the + memcheck and addrcheck skins. +*/ +static +UInt vg_scan_all_valid_memory ( Bool is_valid_64k_chunk ( UInt ), + Bool is_valid_address ( Addr ), + void (*notify_word)( Addr, UInt ) ) +{ + /* All volatile, because some gccs seem paranoid about longjmp(). */ + volatile Addr pageBase, addr; + volatile UInt res, numPages, page, primaryMapNo; + volatile UInt page_first_word, nWordsNotified; + + 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++) { + + /* Base address of this 4k page. */ + pageBase = page << VKI_BYTES_PER_PAGE_BITS; + + /* Skip if this page is in an unused 64k chunk. */ + primaryMapNo = pageBase >> 16; + if (!is_valid_64k_chunk(primaryMapNo)) + continue; + + /* Ok, we have to prod cautiously at the page and see if it + explodes or not. */ + 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) { + if (is_valid_address(addr)) { + 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. +*/ +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 ) + +{ + 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; + /* VG_(printf)("find shadow for %p = ", ptr); */ + retVal = -1; + lo = 0; + hi = n_shadows-1; + while (True) { + /* 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 + vg_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; + + bigN = hi - lo + 1; if (bigN < 2) return; + hp = 0; while (incs[hp] < bigN) hp++; hp--; + + for (; hp >= 0; hp--) { + h = incs[hp]; + i = lo + h; + while (1) { + if (i > hi) break; + v = shadows[i]; + j = i; + while (shadows[j-h]->data > v->data) { + shadows[j] = shadows[j-h]; + j = j - h; + if (j <= (lo + h - 1)) break; + } + shadows[j] = v; + i++; + } + } +} + + +/* Globals, for the callback used by VG_(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; + } + } + } +} + + +/* Top level entry point to leak detector. Call here, passing in + suitable address-validating functions (see comment at top of + vg_scan_all_valid_memory above). All this is to avoid duplication + of the leak-detection code for the Memcheck and Addrcheck skins. + Also pass in a skin-specific function to extract the .where field + for allocated blocks, an indication of the resolution wanted for + distinguishing different allocation points, and whether or not + reachable blocks should be shown. +*/ +void VG_(generic_detect_memory_leaks) ( + Bool is_valid_64k_chunk ( UInt ), + Bool is_valid_address ( Addr ), + ExeContext* get_where ( ShadowChunk* ), + VgRes leak_resolution, + Bool show_reachable +) +{ + 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; + + /* 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 + = VKI_BYTES_PER_WORD + * vg_scan_all_valid_memory ( + is_valid_64k_chunk, + is_valid_address, + &vg_detect_memory_leaks_notify_addr + ); + + 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; extract using function + supplied by the calling skin. */ + 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) ( 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 ( (!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 (!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 ); +} + + /*--------------------------------------------------------------------*/ /*--- end vg_memory.c ---*/ /*--------------------------------------------------------------------*/ diff --git a/include/vg_skin.h b/include/vg_skin.h index 0494277d5d..3d1fee4b4c 100644 --- a/include/vg_skin.h +++ b/include/vg_skin.h @@ -1144,6 +1144,16 @@ extern ShadowChunk* VG_(any_matching_mallocd_ShadowChunks) extern ThreadId VG_(any_matching_thread_stack) ( Bool (*p) ( Addr stack_min, Addr stack_max )); +/* Do memory leak detection. */ +extern void VG_(generic_detect_memory_leaks) ( + Bool is_valid_64k_chunk ( UInt ), + Bool is_valid_address ( Addr ), + ExeContext* get_where ( ShadowChunk* ), + VgRes leak_resolution, + Bool show_reachable + ); + + /*====================================================================*/ /*=== Skin-specific stuff ===*/ /*====================================================================*/ diff --git a/memcheck/mc_main.c b/memcheck/mc_main.c index dcde8dc129..b8c0126107 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 @@ void SK_(alt_free) ( ShadowChunk* sc, ThreadState* tst ) 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 ) - -{ - 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 ) +static +Bool mc_is_valid_64k_chunk ( UInt chunk_number ) { - 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++; - } + 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; } } -/* 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 ) +/* 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 ) { - 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; - } - } + 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) + ); }