1#include <stdio.h>
2#include <stdlib.h>
3#include "leak.h"
4#include "../memcheck.h"
5
6// Pointer chain          AAA Category/output BBB Category/output
7// -------------          ------------------- ------------
8// p1 ---> AAA            DR / R
9// p2 ---> AAA ---> BBB   DR / R              IR / R
10// p3      AAA            DL / L
11// p4      AAA ---> BBB   DL / I              IL / L
12// p5 -?-> AAA            (y)DR, (n)DL / P
13// p6 ---> AAA -?-> BBB   DR / R              (y)IR, (n)DL / P
14// p7 -?-> AAA ---> BBB   (y)DR, (n)DL / P    (y)IR, (n)IL / P
15// p8 -?-> AAA -?-> BBB   (y)DR, (n)DL / P    (y,y)IR, (n,y)IL, (_,n)DL / P
16// p9      AAA -?-> BBB   DL / L              (y)IL, (n)DL / I
17//
18// Pointer chain legend:
19// - pN: a root set pointer
20// - AAA, BBB: heap blocks
21// - --->: a start-pointer
22// - -?->: an interior-pointer
23//
24// Category legend:
25// - DR: Directly reachable
26// - IR: Indirectly reachable
27// - DL: Directly lost
28// - IL: Indirectly lost
29// - (y)XY: it's XY if the interior-pointer is a real pointer
30// - (n)XY: it's XY if the interior-pointer is not a real pointer
31// - (_)XY: it's XY in either case
32//
33// How we handle the 9 cases:
34// - "directly lost":    case 3
35// - "indirectly lost":  cases 4, 9
36// - "possibly lost":    cases 5..8
37// - "still reachable":  cases 1, 2
38
39
40typedef
41   struct _Node {
42      struct _Node* next;
43      // Padding ensures the structu is the same size on 32-bit and 64-bit
44      // machines.
45      char padding[8 - sizeof(struct _Node*)];
46   } Node;
47
48Node* mk(Node* next)
49{
50   // We allocate two nodes, so we can do p+1 and still point within the
51   // block.
52   Node* x = malloc(2 * sizeof(Node));
53   x->next = next;
54   return x;
55}
56
57// These are definite roots.
58Node* p1;
59Node* p2;
60Node* p3;
61Node* p4;
62Node* p5;
63Node* p6;
64Node* p7;
65Node* p8;
66Node* p9;
67
68void f(void)
69{
70   p1 = mk(NULL);       // Case 1: 16/1 still reachable
71
72   p2 = mk(mk(NULL));   // Case 2: 16/1 still reachable
73                                // 16/1 still reachable
74   (void)mk(NULL);      // Case 3: 16/1 definitely lost
75
76   (void)mk(mk(NULL));  // Case 4: 16/1 indirectly lost (counted again below!)
77                                // 32(16d,16i)/1 definitely lost (double count!)
78   p5 = mk(NULL);       // Case 5: 16/1 possibly lost (ok)
79   p5++;
80
81   p6 = mk(mk(NULL));   // Case 6: 16/1 still reachable
82   (p6->next)++;                // 16/1 possibly lost
83
84   p7 = mk(mk(NULL));   // Case 7: 16/1 possibly lost
85   p7++;                        // 16/1 possibly lost
86
87   p8 = mk(mk(NULL));   // Case 8: 16/1 possibly lost
88   (p8->next)++;                // 16/1 possibly lost
89   p8++;
90
91   p9 = mk(mk(NULL));   // Case 9: 16/1 indirectly lost (counted again below!)
92   (p9->next)++;                // 32(16d,16i)/1 definitely lost (double count!)
93   p9 = NULL;
94}
95
96int main(void)
97{
98   DECLARE_LEAK_COUNTERS;
99
100   GET_INITIAL_LEAK_COUNTS;
101
102   // Originally, this program did all the work in main(), but on some
103   // platforms (x86/Darwin and AMD64/Linux with --enable-only32bit) stray
104   // pointers to supposedly-lost heap blocks were being left on the stack,
105   // thus making them reachable.  Doing the allocations in f() and the leak
106   // counting in main() avoids the problem.
107   f();
108
109   CLEAR_CALLER_SAVED_REGS;
110   GET_FINAL_LEAK_COUNTS;
111
112   PRINT_LEAK_COUNTS(stderr);
113
114   return 0;
115}
116