// If 'ptr' is pointing to a heap-allocated block which hasn't been seen // before, push it onto the mark stack. static void lc_push_without_clique_if_a_chunk_ptr(Addr ptr, Bool is_prior_definite) { Int ch_no; MC_Chunk* ch; LC_Extra* ex; if ( ! lc_is_a_chunk_ptr(ptr, &ch_no, &ch, &ex) ) return; // Possibly upgrade the state, ie. one of: // - Unreached --> Possible // - Unreached --> Reachable // - Possible --> Reachable if (ptr == ch->data && is_prior_definite && ex->state != Reachable) { // 'ptr' points to the start of the block, and the prior node is // definite, which means that this block is definitely reachable. ex->state = Reachable; // State has changed to Reachable so (re)scan the block to make // sure any blocks it points to are correctly marked. lc_push(ch_no, ch); } else if (ex->state == Unreached) { // Either 'ptr' is a interior-pointer, or the prior node isn't definite, // which means that we can only mark this block as possibly reachable. ex->state = Possible; // State has changed to Possible so (re)scan the block to make // sure any blocks it points to are correctly marked. lc_push(ch_no, ch); } }
// If 'ptr' is pointing to a heap-allocated block which hasn't been seen // before, push it onto the mark stack. static void lc_push_without_clique_if_a_chunk_ptr(Addr ptr, Bool is_prior_definite) { Int ch_no; MC_Chunk* ch; LC_Extra* ex; if ( ! lc_is_a_chunk_ptr(ptr, &ch_no, &ch, &ex) ) return; // Only push it if it hasn't been seen previously. if (ex->state == Unreached) { lc_push(ch_no, ch); } // Possibly upgrade the state, ie. one of: // - Unreached --> Possible // - Unreached --> Reachable // - Possible --> Reachable if (ptr == ch->data && is_prior_definite) { // 'ptr' points to the start of the block, and the prior node is // definite, which means that this block is definitely reachable. ex->state = Reachable; } else if (ex->state == Unreached) { // Either 'ptr' is a interior-pointer, or the prior node isn't definite, // which means that we can only mark this block as possibly reachable. ex->state = Possible; } }
void serialize_add(char *name, char *content, char *alias) { srlz *s; serialize_init(); if ((s = ALLOC(*s)) == NULL) perror("malloc"); s->name = name; s->content = content; s->alias = alias; lc_push(_serializes_list, s); return ; }
// If ptr is pointing to a heap-allocated block which hasn't been seen // before, push it onto the mark stack. Clique is the index of the // clique leader. static void lc_push_with_clique_if_a_chunk_ptr(Addr ptr, Int clique) { Int ch_no; MC_Chunk* ch; LC_Extra* ex; tl_assert(0 <= clique && clique < lc_n_chunks); if ( ! lc_is_a_chunk_ptr(ptr, &ch_no, &ch, &ex) ) return; // If it's not Unreached, it's already been handled so ignore it. // If ch_no==clique, it's the clique leader, which means this is a cyclic // structure; again ignore it because it's already been handled. if (ex->state == Unreached && ch_no != clique) { // Note that, unlike reachable blocks, we currently don't distinguish // between start-pointers and interior-pointers here. We probably // should, though. ex->state = IndirectLeak; lc_push(ch_no, ch); // Add the block to the clique, and add its size to the // clique-leader's indirect size. Also, if the new block was // itself a clique leader, it isn't any more, so add its // indirect_szB to the new clique leader. if (VG_DEBUG_CLIQUE) { if (ex->indirect_szB > 0) VG_(printf)(" clique %d joining clique %d adding %lu+%lu\n", ch_no, clique, (unsigned long)ch->szB, (unsigned long)ex->indirect_szB); else VG_(printf)(" block %d joining clique %d adding %lu\n", ch_no, clique, (unsigned long)ch->szB); } lc_extras[clique].indirect_szB += ch->szB; lc_extras[clique].indirect_szB += ex->indirect_szB; ex->indirect_szB = 0; // Shouldn't matter. } }
void MC_(detect_memory_leaks) ( ThreadId tid, LeakCheckMode mode ) { Int i; tl_assert(mode != LC_Off); // Get the chunks, stop if there were none. lc_chunks = find_active_chunks(&lc_n_chunks); if (lc_n_chunks == 0) { tl_assert(lc_chunks == NULL); if (VG_(clo_verbosity) >= 1 && !VG_(clo_xml)) { VG_(UMSG)("All heap blocks were freed -- no leaks are possible.\n"); } return; } // Sort the array so blocks are in ascending order in memory. VG_(ssort)(lc_chunks, lc_n_chunks, sizeof(VgHashNode*), compare_MC_Chunks); // Sanity check -- make sure they're in order. for (i = 0; i < lc_n_chunks-1; i++) { tl_assert( lc_chunks[i]->data <= lc_chunks[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_chunks-1; i++) { MC_Chunk* ch1 = lc_chunks[i]; MC_Chunk* ch2 = lc_chunks[i+1]; Bool nonsense_overlap = ! ( // Normal case - no overlap. (ch1->data + ch1->szB <= ch2->data) || // Degenerate case: exact duplicates. (ch1->data == ch2->data && ch1->szB == ch2->szB) ); if (nonsense_overlap) { VG_(UMSG)("Block [0x%lx, 0x%lx) overlaps with block [0x%lx, 0x%lx)\n", ch1->data, (ch1->data + ch1->szB), ch2->data, (ch2->data + ch2->szB)); } tl_assert (!nonsense_overlap); } // Initialise lc_extras. lc_extras = VG_(malloc)( "mc.dml.2", lc_n_chunks * sizeof(LC_Extra) ); for (i = 0; i < lc_n_chunks; i++) { lc_extras[i].state = Unreached; lc_extras[i].indirect_szB = 0; } // Initialise lc_markstack. lc_markstack = VG_(malloc)( "mc.dml.2", lc_n_chunks * sizeof(Int) ); for (i = 0; i < lc_n_chunks; i++) { lc_markstack[i] = -1; } lc_markstack_top = -1; // Verbosity. if (VG_(clo_verbosity) > 0 && !VG_(clo_xml)) VG_(UMSG)( "searching for pointers to %'d not-freed blocks.\n", lc_n_chunks ); // Scan the memory root-set, pushing onto the mark stack any blocks // pointed to. { Int n_seg_starts; Addr* seg_starts = VG_(get_segment_starts)( &n_seg_starts ); tl_assert(seg_starts && n_seg_starts > 0); lc_scanned_szB = 0; // VG_(am_show_nsegments)( 0, "leakcheck"); for (i = 0; i < n_seg_starts; i++) { SizeT seg_size; 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); // Scan the segment. We use -1 for the clique number, because this // is a root-set. seg_size = seg->end - seg->start + 1; if (VG_(clo_verbosity) > 2) { VG_(message)(Vg_DebugMsg, " Scanning root segment: %#lx..%#lx (%lu)\n", seg->start, seg->end, seg_size); } lc_scan_memory(seg->start, seg_size, /*is_prior_definite*/True, -1); } } // Scan GP registers for chunk pointers. VG_(apply_to_GP_regs)(lc_push_if_a_chunk_ptr_register); // Process the pushed blocks. After this, every block that is reachable // from the root-set has been traced. lc_process_markstack(/*clique*/-1); if (VG_(clo_verbosity) > 0 && !VG_(clo_xml)) VG_(UMSG)("checked %'lu bytes.\n", lc_scanned_szB); // Trace all the leaked blocks to determine which are directly leaked and // which are indirectly leaked. For each Unreached block, push it onto // the mark stack, and find all the as-yet-Unreached blocks reachable // from it. These form a clique and are marked IndirectLeak, and their // size is added to the clique leader's indirect size. If one of the // found blocks was itself a clique leader (from a previous clique), then // the cliques are merged. for (i = 0; i < lc_n_chunks; i++) { MC_Chunk* ch = lc_chunks[i]; LC_Extra* ex = &(lc_extras[i]); if (VG_DEBUG_CLIQUE) VG_(printf)("cliques: %d at %#lx -> Loss state %d\n", i, ch->data, ex->state); tl_assert(lc_markstack_top == -1); if (ex->state == Unreached) { if (VG_DEBUG_CLIQUE) VG_(printf)("%d: gathering clique %#lx\n", i, ch->data); // Push this Unreached block onto the stack and process it. lc_push(i, ch); lc_process_markstack(i); tl_assert(lc_markstack_top == -1); tl_assert(ex->state == Unreached); } } print_results( tid, ( mode == LC_Full ? True : False ) ); VG_(free) ( lc_chunks ); VG_(free) ( lc_extras ); VG_(free) ( lc_markstack ); }
void MC_(detect_memory_leaks) ( ThreadId tid, LeakCheckMode mode ) { Int i, j; tl_assert(mode != LC_Off); // Get the chunks, stop if there were none. lc_chunks = find_active_chunks(&lc_n_chunks); if (lc_n_chunks == 0) { tl_assert(lc_chunks == NULL); if (VG_(clo_verbosity) >= 1 && !VG_(clo_xml)) { VG_(umsg)("All heap blocks were freed -- no leaks are possible\n"); VG_(umsg)("\n"); } return; } // Sort the array so blocks are in ascending order in memory. VG_(ssort)(lc_chunks, lc_n_chunks, sizeof(VgHashNode*), compare_MC_Chunks); // Sanity check -- make sure they're in order. for (i = 0; i < lc_n_chunks-1; i++) { tl_assert( lc_chunks[i]->data <= lc_chunks[i+1]->data); } // Sanity check -- make sure they don't overlap. The one exception is that // we allow a MALLOCLIKE block to sit entirely within a malloc() block. // This is for bug 100628. If this occurs, we ignore the malloc() block // for leak-checking purposes. This is a hack and probably should be done // better, but at least it's consistent with mempools (which are treated // like this in find_active_chunks). Mempools have a separate VgHashTable // for mempool chunks, but if custom-allocated blocks are put in a separate // table from normal heap blocks it makes free-mismatch checking more // difficult. // // If this check fails, it probably means that the application // has done something stupid with VALGRIND_MALLOCLIKE_BLOCK client // requests, eg. has made overlapping requests (which are // nonsensical), or used VALGRIND_MALLOCLIKE_BLOCK for stack locations; // again nonsensical. // for (i = 0; i < lc_n_chunks-1; i++) { MC_Chunk* ch1 = lc_chunks[i]; MC_Chunk* ch2 = lc_chunks[i+1]; Addr start1 = ch1->data; Addr start2 = ch2->data; Addr end1 = ch1->data + ch1->szB - 1; Addr end2 = ch2->data + ch2->szB - 1; Bool isCustom1 = ch1->allockind == MC_AllocCustom; Bool isCustom2 = ch2->allockind == MC_AllocCustom; if (end1 < start2) { // Normal case - no overlap. // We used to allow exact duplicates, I'm not sure why. --njn //} else if (start1 == start2 && end1 == end2) { // Degenerate case: exact duplicates. } else if (start1 >= start2 && end1 <= end2 && isCustom1 && !isCustom2) { // Block i is MALLOCLIKE and entirely within block i+1. // Remove block i+1. for (j = i+1; j < lc_n_chunks-1; j++) { lc_chunks[j] = lc_chunks[j+1]; } lc_n_chunks--; } else if (start2 >= start1 && end2 <= end1 && isCustom2 && !isCustom1) { // Block i+1 is MALLOCLIKE and entirely within block i. // Remove block i. for (j = i; j < lc_n_chunks-1; j++) { lc_chunks[j] = lc_chunks[j+1]; } lc_n_chunks--; } else { VG_(umsg)("Block 0x%lx..0x%lx overlaps with block 0x%lx..0x%lx", start1, end1, start1, end2); VG_(umsg)("This is usually caused by using VALGRIND_MALLOCLIKE_BLOCK"); VG_(umsg)("in an inappropriate way."); tl_assert (0); } } // Initialise lc_extras. lc_extras = VG_(malloc)( "mc.dml.2", lc_n_chunks * sizeof(LC_Extra) ); for (i = 0; i < lc_n_chunks; i++) { lc_extras[i].state = Unreached; lc_extras[i].indirect_szB = 0; } // Initialise lc_markstack. lc_markstack = VG_(malloc)( "mc.dml.2", lc_n_chunks * sizeof(Int) ); for (i = 0; i < lc_n_chunks; i++) { lc_markstack[i] = -1; } lc_markstack_top = -1; // Verbosity. if (VG_(clo_verbosity) > 1 && !VG_(clo_xml)) { VG_(umsg)( "Searching for pointers to %'d not-freed blocks\n", lc_n_chunks ); } // Scan the memory root-set, pushing onto the mark stack any blocks // pointed to. { Int n_seg_starts; Addr* seg_starts = VG_(get_segment_starts)( &n_seg_starts ); tl_assert(seg_starts && n_seg_starts > 0); lc_scanned_szB = 0; // VG_(am_show_nsegments)( 0, "leakcheck"); for (i = 0; i < n_seg_starts; i++) { SizeT seg_size; 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); // Scan the segment. We use -1 for the clique number, because this // is a root-set. seg_size = seg->end - seg->start + 1; if (VG_(clo_verbosity) > 2) { VG_(message)(Vg_DebugMsg, " Scanning root segment: %#lx..%#lx (%lu)\n", seg->start, seg->end, seg_size); } lc_scan_memory(seg->start, seg_size, /*is_prior_definite*/True, -1); } } // Scan GP registers for chunk pointers. VG_(apply_to_GP_regs)(lc_push_if_a_chunk_ptr_register); // Process the pushed blocks. After this, every block that is reachable // from the root-set has been traced. lc_process_markstack(/*clique*/-1); if (VG_(clo_verbosity) > 1 && !VG_(clo_xml)) { VG_(umsg)("Checked %'lu bytes\n", lc_scanned_szB); VG_(umsg)( "\n" ); } // Trace all the leaked blocks to determine which are directly leaked and // which are indirectly leaked. For each Unreached block, push it onto // the mark stack, and find all the as-yet-Unreached blocks reachable // from it. These form a clique and are marked IndirectLeak, and their // size is added to the clique leader's indirect size. If one of the // found blocks was itself a clique leader (from a previous clique), then // the cliques are merged. for (i = 0; i < lc_n_chunks; i++) { MC_Chunk* ch = lc_chunks[i]; LC_Extra* ex = &(lc_extras[i]); if (VG_DEBUG_CLIQUE) VG_(printf)("cliques: %d at %#lx -> Loss state %d\n", i, ch->data, ex->state); tl_assert(lc_markstack_top == -1); if (ex->state == Unreached) { if (VG_DEBUG_CLIQUE) VG_(printf)("%d: gathering clique %#lx\n", i, ch->data); // Push this Unreached block onto the stack and process it. lc_push(i, ch); lc_process_markstack(i); tl_assert(lc_markstack_top == -1); tl_assert(ex->state == Unreached); } } print_results( tid, ( mode == LC_Full ? True : False ) ); VG_(free) ( lc_chunks ); VG_(free) ( lc_extras ); VG_(free) ( lc_markstack ); }