/* Top level entry point to leak detector. Call here, passing in suitable address-validating functions (see comment at top of scan_all_valid_memory above). These functions used to encapsulate the differences between Memcheck and Addrcheck; they no longer do but it doesn't hurt to keep them here. */ void MC_(do_detect_memory_leaks) ( ThreadId tid, LeakCheckMode mode, Bool (*is_within_valid_secondary) ( Addr ), Bool (*is_valid_aligned_word) ( Addr ) ) { Int i; tl_assert(mode != LC_Off); lc_shadows = find_active_shadows(&lc_n_shadows); /* Sort the array. */ VG_(ssort)((void*)lc_shadows, lc_n_shadows, sizeof(VgHashNode*), lc_compar); /* Sanity check; assert that the blocks are now in order */ for (i = 0; i < lc_n_shadows-1; i++) { tl_assert( lc_shadows[i]->data <= lc_shadows[i+1]->data); } /* Sanity check -- make sure they don't overlap. But do allow exact duplicates. If this assertion fails, it may mean that the application has done something stupid with VALGRIND_MALLOCLIKE_BLOCK client requests, specifically, has made overlapping requests (which are nonsensical). Another way to screw up is to use VALGRIND_MALLOCLIKE_BLOCK for stack locations; again nonsensical. */ for (i = 0; i < lc_n_shadows-1; i++) { Bool nonsense_overlap = ! ( /* normal case - no overlap */ (lc_shadows[i]->data + lc_shadows[i]->szB <= lc_shadows[i+1]->data) || /* degenerate case: exact duplicates */ (lc_shadows[i]->data == lc_shadows[i+1]->data && lc_shadows[i]->szB == lc_shadows[i+1]->szB) ); if (nonsense_overlap) { VG_(message)(Vg_UserMsg, "Block [0x%lx, 0x%lx) overlaps with block [0x%lx, 0x%lx)", lc_shadows[ i]->data, (lc_shadows[ i]->data + lc_shadows[ i]->szB), lc_shadows[1+ i]->data, (lc_shadows[1+ i]->data + lc_shadows[1+ i]->szB) ); } tl_assert (!nonsense_overlap); } if (lc_n_shadows == 0) { tl_assert(lc_shadows == NULL); if (VG_(clo_verbosity) >= 1 && !VG_(clo_xml)) { VG_(message)(Vg_UserMsg, "All heap blocks were freed -- no leaks are possible."); } return; } if (VG_(clo_verbosity) > 0 && !VG_(clo_xml)) VG_(message)(Vg_UserMsg, "searching for pointers to %'d not-freed blocks.", lc_n_shadows ); lc_min_mallocd_addr = lc_shadows[0]->data; lc_max_mallocd_addr = lc_shadows[lc_n_shadows-1]->data + lc_shadows[lc_n_shadows-1]->szB; lc_markstack = VG_(malloc)( "mc.ddml.1", lc_n_shadows * sizeof(*lc_markstack) ); for (i = 0; i < lc_n_shadows; i++) { lc_markstack[i].next = -1; lc_markstack[i].state = Unreached; lc_markstack[i].indirect = 0; } lc_markstack_top = -1; lc_is_within_valid_secondary = is_within_valid_secondary; lc_is_valid_aligned_word = is_valid_aligned_word; lc_scanned = 0; /* Push roots onto the mark stack. Roots are: - the integer registers of all threads - all mappings belonging to the client, including stacks - .. but excluding any client heap segments. Client heap segments are excluded because we wish to differentiate client heap blocks which are referenced only from inside the heap from those outside. This facilitates the indirect vs direct loss categorisation, which [if the users ever manage to understand it] is really useful for detecting lost cycles. */ { Addr* seg_starts; Int n_seg_starts; seg_starts = get_seg_starts( &n_seg_starts ); tl_assert(seg_starts && n_seg_starts > 0); /* VG_(am_show_nsegments)( 0,"leakcheck"); */ for (i = 0; i < n_seg_starts; i++) { NSegment const* seg = VG_(am_find_nsegment)( seg_starts[i] ); tl_assert(seg); if (seg->kind != SkFileC && seg->kind != SkAnonC) continue; if (!(seg->hasR && seg->hasW)) continue; if (seg->isCH) continue; /* Don't poke around in device segments as this may cause hangs. Exclude /dev/zero just in case someone allocated memory by explicitly mapping /dev/zero. */ if (seg->kind == SkFileC && (VKI_S_ISCHR(seg->mode) || VKI_S_ISBLK(seg->mode))) { HChar* dev_name = VG_(am_get_filename)( (NSegment*)seg ); if (dev_name && 0 == VG_(strcmp)(dev_name, "/dev/zero")) { /* don't skip /dev/zero */ } else { /* skip this device mapping */ continue; } } if (0) VG_(printf)("ACCEPT %2d %#lx %#lx\n", i, seg->start, seg->end); lc_scan_memory(seg->start, seg->end+1 - seg->start); } } /* Push registers onto mark stack */ VG_(apply_to_GP_regs)(lc_markstack_push); /* Keep walking the heap until everything is found */ lc_do_leakcheck(-1); if (VG_(clo_verbosity) > 0 && !VG_(clo_xml)) VG_(message)(Vg_UserMsg, "checked %'lu bytes.", lc_scanned); blocks_leaked = MC_(bytes_leaked) = 0; blocks_indirect = MC_(bytes_indirect) = 0; blocks_dubious = MC_(bytes_dubious) = 0; blocks_reachable = MC_(bytes_reachable) = 0; blocks_suppressed = MC_(bytes_suppressed) = 0; if (mode == LC_Full) full_report(tid); else make_summary(); if (VG_(clo_verbosity) > 0 && !VG_(clo_xml)) { VG_(message)(Vg_UserMsg, ""); VG_(message)(Vg_UserMsg, "LEAK SUMMARY:"); VG_(message)(Vg_UserMsg, " definitely lost: %'lu bytes in %'lu blocks.", MC_(bytes_leaked), blocks_leaked ); if (blocks_indirect > 0) VG_(message)(Vg_UserMsg, " indirectly lost: %'lu bytes in %'lu blocks.", MC_(bytes_indirect), blocks_indirect ); VG_(message)(Vg_UserMsg, " possibly lost: %'lu bytes in %'lu blocks.", MC_(bytes_dubious), blocks_dubious ); VG_(message)(Vg_UserMsg, " still reachable: %'lu bytes in %'lu blocks.", MC_(bytes_reachable), blocks_reachable ); VG_(message)(Vg_UserMsg, " suppressed: %'lu bytes in %'lu blocks.", MC_(bytes_suppressed), blocks_suppressed ); if (mode == LC_Summary && (blocks_leaked + blocks_indirect + blocks_dubious + blocks_reachable) > 0) { VG_(message)(Vg_UserMsg, "Rerun with --leak-check=full to see details of leaked memory."); } if (blocks_reachable > 0 && !MC_(clo_show_reachable) && mode == LC_Full) { 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: --leak-check=full --show-reachable=yes"); } } VG_(free) ( lc_shadows ); VG_(free) ( lc_markstack ); }
/* Top level entry point to leak detector. Call here, passing in suitable address-validating functions (see comment at top of scan_all_valid_memory above). All this is to avoid duplication of the leak-detection code for Memcheck and Addrcheck. Also pass in a tool-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 MAC_(do_detect_memory_leaks) ( ThreadId tid, LeakCheckMode mode, Bool (*is_within_valid_secondary) ( Addr ), Bool (*is_valid_aligned_word) ( Addr ) ) { Int i; tl_assert(mode != LC_Off); /* VG_(HT_to_array) allocates storage for shadows */ lc_shadows = (MAC_Chunk**)VG_(HT_to_array)( MAC_(malloc_list), &lc_n_shadows ); /* Sort the array. */ VG_(ssort)((void*)lc_shadows, lc_n_shadows, sizeof(VgHashNode*), lc_compar); /* Sanity check; assert that the blocks are now in order */ for (i = 0; i < lc_n_shadows-1; i++) { tl_assert( lc_shadows[i]->data <= lc_shadows[i+1]->data); } /* Sanity check -- make sure they don't overlap */ for (i = 0; i < lc_n_shadows-1; i++) { tl_assert( lc_shadows[i]->data + lc_shadows[i]->size < lc_shadows[i+1]->data ); } if (lc_n_shadows == 0) { tl_assert(lc_shadows == NULL); if (VG_(clo_verbosity) >= 1 && !VG_(clo_xml)) { VG_(message)(Vg_UserMsg, "No malloc'd blocks -- no leaks are possible."); } return; } if (VG_(clo_verbosity) > 0 && !VG_(clo_xml)) VG_(message)(Vg_UserMsg, "searching for pointers to %d not-freed blocks.", lc_n_shadows ); lc_min_mallocd_addr = lc_shadows[0]->data; lc_max_mallocd_addr = lc_shadows[lc_n_shadows-1]->data + lc_shadows[lc_n_shadows-1]->size; lc_markstack = VG_(malloc)( lc_n_shadows * sizeof(*lc_markstack) ); for (i = 0; i < lc_n_shadows; i++) { lc_markstack[i].next = -1; lc_markstack[i].state = Unreached; lc_markstack[i].indirect = 0; } lc_markstack_top = -1; lc_is_within_valid_secondary = is_within_valid_secondary; lc_is_valid_aligned_word = is_valid_aligned_word; lc_scanned = 0; /* Push roots onto the mark stack. Roots are: - the integer registers of all threads - all mappings belonging to the client, including stacks - .. but excluding any client heap segments. Client heap segments are excluded because we wish to differentiate client heap blocks which are referenced only from inside the heap from those outside. This facilitates the indirect vs direct loss categorisation, which [if the users ever manage to understand it] is really useful for detecting lost cycles. */ { NSegment* seg; Addr* seg_starts; Int n_seg_starts; seg_starts = get_seg_starts( &n_seg_starts ); tl_assert(seg_starts && n_seg_starts > 0); /* VG_(am_show_nsegments)( 0,"leakcheck"); */ for (i = 0; i < n_seg_starts; i++) { seg = VG_(am_find_nsegment)( seg_starts[i] ); tl_assert(seg); if (seg->kind != SkFileC && seg->kind != SkAnonC) continue; if (!(seg->hasR && seg->hasW)) continue; if (seg->isCH) continue; if (0) VG_(printf)("ACCEPT %2d %p %p\n", i, seg->start, seg->end); lc_scan_memory(seg->start, seg->end+1 - seg->start); } } /* Push registers onto mark stack */ VG_(apply_to_GP_regs)(lc_markstack_push); /* Keep walking the heap until everything is found */ lc_do_leakcheck(-1); if (VG_(clo_verbosity) > 0 && !VG_(clo_xml)) VG_(message)(Vg_UserMsg, "checked %d bytes.", lc_scanned); blocks_leaked = MAC_(bytes_leaked) = 0; blocks_indirect = MAC_(bytes_indirect) = 0; blocks_dubious = MAC_(bytes_dubious) = 0; blocks_reachable = MAC_(bytes_reachable) = 0; blocks_suppressed = MAC_(bytes_suppressed) = 0; if (mode == LC_Full) full_report(tid); else make_summary(); if (VG_(clo_verbosity) > 0 && !VG_(clo_xml)) { VG_(message)(Vg_UserMsg, ""); VG_(message)(Vg_UserMsg, "LEAK SUMMARY:"); VG_(message)(Vg_UserMsg, " definitely lost: %d bytes in %d blocks.", MAC_(bytes_leaked), blocks_leaked ); if (blocks_indirect > 0) VG_(message)(Vg_UserMsg, " indirectly lost: %d bytes in %d blocks.", MAC_(bytes_indirect), blocks_indirect ); VG_(message)(Vg_UserMsg, " possibly lost: %d bytes in %d blocks.", MAC_(bytes_dubious), blocks_dubious ); VG_(message)(Vg_UserMsg, " still reachable: %d bytes in %d blocks.", MAC_(bytes_reachable), blocks_reachable ); VG_(message)(Vg_UserMsg, " suppressed: %d bytes in %d blocks.", MAC_(bytes_suppressed), blocks_suppressed ); if (mode == LC_Summary && blocks_leaked > 0) VG_(message)(Vg_UserMsg, "Use --leak-check=full to see details of leaked memory."); else if (!MAC_(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_(free) ( lc_shadows ); VG_(free) ( lc_markstack ); }
/* Event handler for tool 'error-support' */ ATerm error_support_handler(int conn, ATerm term) { ATerm in, out; /* We need some temporary variables during matching */ char *s0, *s1; ATerm t0; if(ATmatch(term, "rec-eval(make-error(<str>,<term>))", &s0, &t0)) { return make_error(conn, s0, t0); } if(ATmatch(term, "rec-eval(get-area-end-line(<term>))", &t0)) { return get_area_end_line(conn, t0); } if(ATmatch(term, "rec-eval(get-location-filename(<term>))", &t0)) { return get_location_filename(conn, t0); } if(ATmatch(term, "rec-eval(has-location-area(<term>))", &t0)) { return has_location_area(conn, t0); } if(ATmatch(term, "rec-eval(get-summary-id(<term>))", &t0)) { return get_summary_id(conn, t0); } if(ATmatch(term, "rec-eval(get-error-description(<term>))", &t0)) { return get_error_description(conn, t0); } if(ATmatch(term, "rec-do(display-summary(<term>))", &t0)) { display_summary(conn, t0); return NULL; } if(ATmatch(term, "rec-eval(lower-summary(<term>))", &t0)) { return lower_summary(conn, t0); } if(ATmatch(term, "rec-eval(make-localized-subject(<str>,<term>))", &s0, &t0)) { return make_localized_subject(conn, s0, t0); } if(ATmatch(term, "rec-eval(get-summary-producer(<term>))", &t0)) { return get_summary_producer(conn, t0); } if(ATmatch(term, "rec-eval(get-error-subjects(<term>))", &t0)) { return get_error_subjects(conn, t0); } if(ATmatch(term, "rec-eval(make-subject(<str>))", &s0)) { return make_subject(conn, s0); } if(ATmatch(term, "rec-eval(get-subject-location(<term>))", &t0)) { return get_subject_location(conn, t0); } if(ATmatch(term, "rec-eval(get-area-offset(<term>))", &t0)) { return get_area_offset(conn, t0); } if(ATmatch(term, "rec-eval(get-area-begin-line(<term>))", &t0)) { return get_area_begin_line(conn, t0); } if(ATmatch(term, "rec-eval(add-filename-in-error(<str>,<term>))", &s0, &t0)) { return add_filename_in_error(conn, s0, t0); } if(ATmatch(term, "rec-eval(get-location-area(<term>))", &t0)) { return get_location_area(conn, t0); } if(ATmatch(term, "rec-eval(set-summary-id(<term>,<str>))", &t0, &s0)) { return set_summary_id(conn, t0, s0); } if(ATmatch(term, "rec-eval(has-subject-location(<term>))", &t0)) { return has_subject_location(conn, t0); } if(ATmatch(term, "rec-eval(get-area-end-column(<term>))", &t0)) { return get_area_end_column(conn, t0); } if(ATmatch(term, "rec-eval(make-summary(<str>,<str>,<term>))", &s0, &s1, &t0)) { return make_summary(conn, s0, s1, t0); } if(ATmatch(term, "rec-eval(get-summary-errors(<term>))", &t0)) { return get_summary_errors(conn, t0); } if(ATmatch(term, "rec-eval(get-subject-description(<term>))", &t0)) { return get_subject_description(conn, t0); } if(ATmatch(term, "rec-eval(get-area-length(<term>))", &t0)) { return get_area_length(conn, t0); } if(ATmatch(term, "rec-terminate(<term>)", &t0)) { rec_terminate(conn, t0); return NULL; } if(ATmatch(term, "rec-eval(get-area-begin-column(<term>))", &t0)) { return get_area_begin_column(conn, t0); } if(ATmatch(term, "rec-do(signature(<term>,<term>))", &in, &out)) { ATerm result = error_support_checker(conn, in); if(!ATmatch(result, "[]")) ATfprintf(stderr, "warning: not in input signature:\n\t%\n\tl\n", result); return NULL; } ATerror("tool error-support cannot handle term %t", term); return NULL; /* Silence the compiler */ }
/* Top level entry point to leak detector. Call here, passing in suitable address-validating functions (see comment at top of scan_all_valid_memory above). All this is to avoid duplication of the leak-detection code for Memcheck and Addrcheck. Also pass in a tool-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 MAC_(do_detect_memory_leaks) ( ThreadId tid, LeakCheckMode mode, Bool (*is_within_valid_secondary) ( Addr ), Bool (*is_valid_aligned_word) ( Addr ) ) { Int i; tl_assert(mode != LC_Off); /* VG_(HT_to_array) allocates storage for shadows */ lc_shadows = (MAC_Chunk**)VG_(HT_to_array)( MAC_(malloc_list), &lc_n_shadows ); /* Sort the array. */ VG_(ssort)((void*)lc_shadows, lc_n_shadows, sizeof(VgHashNode*), lc_compar); /* Sanity check; assert that the blocks are now in order */ for (i = 0; i < lc_n_shadows-1; i++) { tl_assert( lc_shadows[i]->data <= lc_shadows[i+1]->data); } /* Sanity check -- make sure they don't overlap */ for (i = 0; i < lc_n_shadows-1; i++) { tl_assert( lc_shadows[i]->data + lc_shadows[i]->size < lc_shadows[i+1]->data ); } if (lc_n_shadows == 0) { tl_assert(lc_shadows == NULL); if (VG_(clo_verbosity) >= 1 && !VG_(clo_xml)) { VG_(message)(Vg_UserMsg, "No malloc'd blocks -- no leaks are possible."); } return; } if (VG_(clo_verbosity) > 0 && !VG_(clo_xml)) VG_(message)(Vg_UserMsg, "searching for pointers to %d not-freed blocks.", lc_n_shadows ); lc_min_mallocd_addr = lc_shadows[0]->data; lc_max_mallocd_addr = lc_shadows[lc_n_shadows-1]->data + lc_shadows[lc_n_shadows-1]->size; lc_markstack = VG_(malloc)( lc_n_shadows * sizeof(*lc_markstack) ); for (i = 0; i < lc_n_shadows; i++) { lc_markstack[i].next = -1; lc_markstack[i].state = Unreached; lc_markstack[i].indirect = 0; } lc_markstack_top = -1; lc_is_within_valid_secondary = is_within_valid_secondary; lc_is_valid_aligned_word = is_valid_aligned_word; lc_scanned = 0; /* Do the scan of memory, pushing any pointers onto the mark stack */ VG_(find_root_memory)(lc_scan_memory); /* Push registers onto mark stack */ VG_(apply_to_GP_regs)(lc_markstack_push); /* Keep walking the heap until everything is found */ lc_do_leakcheck(-1); if (VG_(clo_verbosity) > 0 && !VG_(clo_xml)) VG_(message)(Vg_UserMsg, "checked %d bytes.", lc_scanned); blocks_leaked = MAC_(bytes_leaked) = 0; blocks_indirect = MAC_(bytes_indirect) = 0; blocks_dubious = MAC_(bytes_dubious) = 0; blocks_reachable = MAC_(bytes_reachable) = 0; blocks_suppressed = MAC_(bytes_suppressed) = 0; if (mode == LC_Full) full_report(tid); else make_summary(); if (VG_(clo_verbosity) > 0 && !VG_(clo_xml)) { VG_(message)(Vg_UserMsg, ""); VG_(message)(Vg_UserMsg, "LEAK SUMMARY:"); VG_(message)(Vg_UserMsg, " definitely lost: %d bytes in %d blocks.", MAC_(bytes_leaked), blocks_leaked ); if (blocks_indirect > 0) VG_(message)(Vg_UserMsg, " indirectly lost: %d bytes in %d blocks.", MAC_(bytes_indirect), blocks_indirect ); VG_(message)(Vg_UserMsg, " possibly lost: %d bytes in %d blocks.", MAC_(bytes_dubious), blocks_dubious ); VG_(message)(Vg_UserMsg, " still reachable: %d bytes in %d blocks.", MAC_(bytes_reachable), blocks_reachable ); VG_(message)(Vg_UserMsg, " suppressed: %d bytes in %d blocks.", MAC_(bytes_suppressed), blocks_suppressed ); if (mode == LC_Summary && blocks_leaked > 0) VG_(message)(Vg_UserMsg, "Use --leak-check=full to see details of leaked memory."); else if (!MAC_(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_(free) ( lc_shadows ); VG_(free) ( lc_markstack ); }