}
-
-/*------------------------------------------------------------*/
-/*--- 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)
+ );
}
#include "vg_include.h"
+/* Define to debug the memory-leak-detector. */
+/* #define VG_DEBUG_LEAKCHECK */
+
/*--------------------------------------------------------------*/
/*--- Initialise program data/text etc on program startup. ---*/
}
+/*--------------------------------------------------------------------*/
+/*--- 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 ---*/
/*--------------------------------------------------------------------*/
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 ===*/
/*====================================================================*/
/* 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 */
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)
+ );
}