#include <stdio.h> #include <stdlib.h> #include "leak.h" #include "../memcheck.h" // Pointer chain AAA Category/output BBB Category/output // ------------- ------------------- ------------ // p1 ---> AAA DR / R // p2 ---> AAA ---> BBB DR / R IR / R // p3 AAA DL / L // p4 AAA ---> BBB DL / I IL / L // p5 -?-> AAA (y)DR, (n)DL / P // p6 ---> AAA -?-> BBB DR / R (y)IR, (n)DL / P // p7 -?-> AAA ---> BBB (y)DR, (n)DL / P (y)IR, (n)IL / P // p8 -?-> AAA -?-> BBB (y)DR, (n)DL / P (y,y)IR, (n,y)IL, (_,n)DL / P // p9 AAA -?-> BBB DL / L (y)IL, (n)DL / I // // Pointer chain legend: // - pN: a root set pointer // - AAA, BBB: heap blocks // - --->: a start-pointer // - -?->: an interior-pointer // // Category legend: // - DR: Directly reachable // - IR: Indirectly reachable // - DL: Directly lost // - IL: Indirectly lost // - (y)XY: it's XY if the interior-pointer is a real pointer // - (n)XY: it's XY if the interior-pointer is not a real pointer // - (_)XY: it's XY in either case // // How we handle the 9 cases: // - "directly lost": case 3 // - "indirectly lost": cases 4, 9 // - "possibly lost": cases 5..8 // - "still reachable": cases 1, 2 typedef struct _Node { struct _Node* next; // Padding ensures the structu is the same size on 32-bit and 64-bit // machines. char padding[8 - sizeof(struct _Node*)]; } Node; Node* mk(Node* next) { // We allocate two nodes, so we can do p+1 and still point within the // block. Node* x = malloc(2 * sizeof(Node)); x->next = next; return x; } // These are definite roots. Node* p1; Node* p2; Node* p3; Node* p4; Node* p5; Node* p6; Node* p7; Node* p8; Node* p9; void f(void) { p1 = mk(NULL); // Case 1: 16/1 still reachable p2 = mk(mk(NULL)); // Case 2: 16/1 still reachable // 16/1 still reachable (void)mk(NULL); // Case 3: 16/1 definitely lost (void)mk(mk(NULL)); // Case 4: 16/1 indirectly lost (counted again below!) // 32(16d,16i)/1 definitely lost (double count!) p5 = mk(NULL); // Case 5: 16/1 possibly lost (ok) p5++; p6 = mk(mk(NULL)); // Case 6: 16/1 still reachable (p6->next)++; // 16/1 possibly lost p7 = mk(mk(NULL)); // Case 7: 16/1 possibly lost p7++; // 16/1 possibly lost p8 = mk(mk(NULL)); // Case 8: 16/1 possibly lost (p8->next)++; // 16/1 possibly lost p8++; p9 = mk(mk(NULL)); // Case 9: 16/1 indirectly lost (counted again below!) (p9->next)++; // 32(16d,16i)/1 definitely lost (double count!) p9 = NULL; } int main(void) { DECLARE_LEAK_COUNTERS; GET_INITIAL_LEAK_COUNTS; // Originally, this program did all the work in main(), but on some // platforms (x86/Darwin and AMD64/Linux with --enable-only32bit) stray // pointers to supposedly-lost heap blocks were being left on the stack, // thus making them reachable. Doing the allocations in f() and the leak // counting in main() avoids the problem. f(); CLEAR_CALLER_SAVED_REGS; GET_FINAL_LEAK_COUNTS; PRINT_LEAK_COUNTS(stderr); return 0; }