1e4b17023SJohn Marino /* Simple garbage collection for the GNU compiler.
2e4b17023SJohn Marino Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
3e4b17023SJohn Marino 2009, 2010 Free Software Foundation, Inc.
4e4b17023SJohn Marino
5e4b17023SJohn Marino This file is part of GCC.
6e4b17023SJohn Marino
7e4b17023SJohn Marino GCC is free software; you can redistribute it and/or modify it under
8e4b17023SJohn Marino the terms of the GNU General Public License as published by the Free
9e4b17023SJohn Marino Software Foundation; either version 3, or (at your option) any later
10e4b17023SJohn Marino version.
11e4b17023SJohn Marino
12e4b17023SJohn Marino GCC is distributed in the hope that it will be useful, but WITHOUT ANY
13e4b17023SJohn Marino WARRANTY; without even the implied warranty of MERCHANTABILITY or
14e4b17023SJohn Marino FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15e4b17023SJohn Marino for more details.
16e4b17023SJohn Marino
17e4b17023SJohn Marino You should have received a copy of the GNU General Public License
18e4b17023SJohn Marino along with GCC; see the file COPYING3. If not see
19e4b17023SJohn Marino <http://www.gnu.org/licenses/>. */
20e4b17023SJohn Marino
21e4b17023SJohn Marino /* Generic garbage collection (GC) functions and data, not specific to
22e4b17023SJohn Marino any particular GC implementation. */
23e4b17023SJohn Marino
24e4b17023SJohn Marino #include "config.h"
25e4b17023SJohn Marino #include "system.h"
26e4b17023SJohn Marino #include "coretypes.h"
27e4b17023SJohn Marino #include "hashtab.h"
28e4b17023SJohn Marino #include "ggc.h"
29e4b17023SJohn Marino #include "ggc-internal.h"
30e4b17023SJohn Marino #include "diagnostic-core.h"
31e4b17023SJohn Marino #include "params.h"
32e4b17023SJohn Marino #include "hosthooks.h"
33e4b17023SJohn Marino #include "hosthooks-def.h"
34e4b17023SJohn Marino #include "plugin.h"
35e4b17023SJohn Marino #include "vec.h"
36e4b17023SJohn Marino #include "timevar.h"
37e4b17023SJohn Marino
38e4b17023SJohn Marino /* When set, ggc_collect will do collection. */
39e4b17023SJohn Marino bool ggc_force_collect;
40e4b17023SJohn Marino
41e4b17023SJohn Marino /* When true, protect the contents of the identifier hash table. */
42e4b17023SJohn Marino bool ggc_protect_identifiers = true;
43e4b17023SJohn Marino
44e4b17023SJohn Marino /* Statistics about the allocation. */
45e4b17023SJohn Marino static ggc_statistics *ggc_stats;
46e4b17023SJohn Marino
47e4b17023SJohn Marino struct traversal_state;
48e4b17023SJohn Marino
49e4b17023SJohn Marino static int ggc_htab_delete (void **, void *);
50e4b17023SJohn Marino static hashval_t saving_htab_hash (const void *);
51e4b17023SJohn Marino static int saving_htab_eq (const void *, const void *);
52e4b17023SJohn Marino static int call_count (void **, void *);
53e4b17023SJohn Marino static int call_alloc (void **, void *);
54e4b17023SJohn Marino static int compare_ptr_data (const void *, const void *);
55e4b17023SJohn Marino static void relocate_ptrs (void *, void *);
56e4b17023SJohn Marino static void write_pch_globals (const struct ggc_root_tab * const *tab,
57e4b17023SJohn Marino struct traversal_state *state);
58e4b17023SJohn Marino
59e4b17023SJohn Marino /* Maintain global roots that are preserved during GC. */
60e4b17023SJohn Marino
61e4b17023SJohn Marino /* Process a slot of an htab by deleting it if it has not been marked. */
62e4b17023SJohn Marino
63e4b17023SJohn Marino static int
ggc_htab_delete(void ** slot,void * info)64e4b17023SJohn Marino ggc_htab_delete (void **slot, void *info)
65e4b17023SJohn Marino {
66e4b17023SJohn Marino const struct ggc_cache_tab *r = (const struct ggc_cache_tab *) info;
67e4b17023SJohn Marino
68e4b17023SJohn Marino if (! (*r->marked_p) (*slot))
69e4b17023SJohn Marino htab_clear_slot (*r->base, slot);
70e4b17023SJohn Marino else
71e4b17023SJohn Marino (*r->cb) (*slot);
72e4b17023SJohn Marino
73e4b17023SJohn Marino return 1;
74e4b17023SJohn Marino }
75e4b17023SJohn Marino
76e4b17023SJohn Marino
77e4b17023SJohn Marino /* This extra vector of dynamically registered root_tab-s is used by
78e4b17023SJohn Marino ggc_mark_roots and gives the ability to dynamically add new GGC root
79e4b17023SJohn Marino tables, for instance from some plugins; this vector is on the heap
80e4b17023SJohn Marino since it is used by GGC internally. */
81e4b17023SJohn Marino typedef const struct ggc_root_tab *const_ggc_root_tab_t;
82e4b17023SJohn Marino DEF_VEC_P(const_ggc_root_tab_t);
83e4b17023SJohn Marino DEF_VEC_ALLOC_P(const_ggc_root_tab_t, heap);
VEC(const_ggc_root_tab_t,heap)84e4b17023SJohn Marino static VEC(const_ggc_root_tab_t, heap) *extra_root_vec;
85e4b17023SJohn Marino
86e4b17023SJohn Marino /* Dynamically register a new GGC root table RT. This is useful for
87e4b17023SJohn Marino plugins. */
88e4b17023SJohn Marino
89e4b17023SJohn Marino void
90e4b17023SJohn Marino ggc_register_root_tab (const struct ggc_root_tab* rt)
91e4b17023SJohn Marino {
92e4b17023SJohn Marino if (rt)
93e4b17023SJohn Marino VEC_safe_push (const_ggc_root_tab_t, heap, extra_root_vec, rt);
94e4b17023SJohn Marino }
95e4b17023SJohn Marino
96e4b17023SJohn Marino /* This extra vector of dynamically registered cache_tab-s is used by
97e4b17023SJohn Marino ggc_mark_roots and gives the ability to dynamically add new GGC cache
98e4b17023SJohn Marino tables, for instance from some plugins; this vector is on the heap
99e4b17023SJohn Marino since it is used by GGC internally. */
100e4b17023SJohn Marino typedef const struct ggc_cache_tab *const_ggc_cache_tab_t;
101e4b17023SJohn Marino DEF_VEC_P(const_ggc_cache_tab_t);
102e4b17023SJohn Marino DEF_VEC_ALLOC_P(const_ggc_cache_tab_t, heap);
VEC(const_ggc_cache_tab_t,heap)103e4b17023SJohn Marino static VEC(const_ggc_cache_tab_t, heap) *extra_cache_vec;
104e4b17023SJohn Marino
105e4b17023SJohn Marino /* Dynamically register a new GGC cache table CT. This is useful for
106e4b17023SJohn Marino plugins. */
107e4b17023SJohn Marino
108e4b17023SJohn Marino void
109e4b17023SJohn Marino ggc_register_cache_tab (const struct ggc_cache_tab* ct)
110e4b17023SJohn Marino {
111e4b17023SJohn Marino if (ct)
112e4b17023SJohn Marino VEC_safe_push (const_ggc_cache_tab_t, heap, extra_cache_vec, ct);
113e4b17023SJohn Marino }
114e4b17023SJohn Marino
115e4b17023SJohn Marino /* Scan a hash table that has objects which are to be deleted if they are not
116e4b17023SJohn Marino already marked. */
117e4b17023SJohn Marino
118e4b17023SJohn Marino static void
ggc_scan_cache_tab(const_ggc_cache_tab_t ctp)119e4b17023SJohn Marino ggc_scan_cache_tab (const_ggc_cache_tab_t ctp)
120e4b17023SJohn Marino {
121e4b17023SJohn Marino const struct ggc_cache_tab *cti;
122e4b17023SJohn Marino
123e4b17023SJohn Marino for (cti = ctp; cti->base != NULL; cti++)
124e4b17023SJohn Marino if (*cti->base)
125e4b17023SJohn Marino {
126e4b17023SJohn Marino ggc_set_mark (*cti->base);
127e4b17023SJohn Marino htab_traverse_noresize (*cti->base, ggc_htab_delete,
128e4b17023SJohn Marino CONST_CAST (void *, (const void *)cti));
129e4b17023SJohn Marino ggc_set_mark ((*cti->base)->entries);
130e4b17023SJohn Marino }
131e4b17023SJohn Marino }
132e4b17023SJohn Marino
133e4b17023SJohn Marino /* Mark all the roots in the table RT. */
134e4b17023SJohn Marino
135e4b17023SJohn Marino static void
ggc_mark_root_tab(const_ggc_root_tab_t rt)136e4b17023SJohn Marino ggc_mark_root_tab (const_ggc_root_tab_t rt)
137e4b17023SJohn Marino {
138e4b17023SJohn Marino size_t i;
139e4b17023SJohn Marino
140e4b17023SJohn Marino for ( ; rt->base != NULL; rt++)
141e4b17023SJohn Marino for (i = 0; i < rt->nelt; i++)
142e4b17023SJohn Marino (*rt->cb) (*(void **) ((char *)rt->base + rt->stride * i));
143e4b17023SJohn Marino }
144e4b17023SJohn Marino
145e4b17023SJohn Marino /* Iterate through all registered roots and mark each element. */
146e4b17023SJohn Marino
147e4b17023SJohn Marino void
ggc_mark_roots(void)148e4b17023SJohn Marino ggc_mark_roots (void)
149e4b17023SJohn Marino {
150e4b17023SJohn Marino const struct ggc_root_tab *const *rt;
151e4b17023SJohn Marino const_ggc_root_tab_t rtp, rti;
152e4b17023SJohn Marino const struct ggc_cache_tab *const *ct;
153e4b17023SJohn Marino const_ggc_cache_tab_t ctp;
154e4b17023SJohn Marino size_t i;
155e4b17023SJohn Marino
156e4b17023SJohn Marino for (rt = gt_ggc_deletable_rtab; *rt; rt++)
157e4b17023SJohn Marino for (rti = *rt; rti->base != NULL; rti++)
158e4b17023SJohn Marino memset (rti->base, 0, rti->stride);
159e4b17023SJohn Marino
160e4b17023SJohn Marino for (rt = gt_ggc_rtab; *rt; rt++)
161e4b17023SJohn Marino ggc_mark_root_tab (*rt);
162e4b17023SJohn Marino
163e4b17023SJohn Marino FOR_EACH_VEC_ELT (const_ggc_root_tab_t, extra_root_vec, i, rtp)
164e4b17023SJohn Marino ggc_mark_root_tab (rtp);
165e4b17023SJohn Marino
166e4b17023SJohn Marino if (ggc_protect_identifiers)
167e4b17023SJohn Marino ggc_mark_stringpool ();
168e4b17023SJohn Marino
169e4b17023SJohn Marino /* Now scan all hash tables that have objects which are to be deleted if
170e4b17023SJohn Marino they are not already marked. */
171e4b17023SJohn Marino for (ct = gt_ggc_cache_rtab; *ct; ct++)
172e4b17023SJohn Marino ggc_scan_cache_tab (*ct);
173e4b17023SJohn Marino
174e4b17023SJohn Marino FOR_EACH_VEC_ELT (const_ggc_cache_tab_t, extra_cache_vec, i, ctp)
175e4b17023SJohn Marino ggc_scan_cache_tab (ctp);
176e4b17023SJohn Marino
177e4b17023SJohn Marino if (! ggc_protect_identifiers)
178e4b17023SJohn Marino ggc_purge_stringpool ();
179e4b17023SJohn Marino
180e4b17023SJohn Marino /* Some plugins may call ggc_set_mark from here. */
181e4b17023SJohn Marino invoke_plugin_callbacks (PLUGIN_GGC_MARKING, NULL);
182e4b17023SJohn Marino }
183e4b17023SJohn Marino
184e4b17023SJohn Marino /* Allocate a block of memory, then clear it. */
185e4b17023SJohn Marino void *
ggc_internal_cleared_alloc_stat(size_t size MEM_STAT_DECL)186e4b17023SJohn Marino ggc_internal_cleared_alloc_stat (size_t size MEM_STAT_DECL)
187e4b17023SJohn Marino {
188e4b17023SJohn Marino void *buf = ggc_internal_alloc_stat (size PASS_MEM_STAT);
189e4b17023SJohn Marino memset (buf, 0, size);
190e4b17023SJohn Marino return buf;
191e4b17023SJohn Marino }
192e4b17023SJohn Marino
193e4b17023SJohn Marino /* Resize a block of memory, possibly re-allocating it. */
194e4b17023SJohn Marino void *
ggc_realloc_stat(void * x,size_t size MEM_STAT_DECL)195e4b17023SJohn Marino ggc_realloc_stat (void *x, size_t size MEM_STAT_DECL)
196e4b17023SJohn Marino {
197e4b17023SJohn Marino void *r;
198e4b17023SJohn Marino size_t old_size;
199e4b17023SJohn Marino
200e4b17023SJohn Marino if (x == NULL)
201e4b17023SJohn Marino return ggc_internal_alloc_stat (size PASS_MEM_STAT);
202e4b17023SJohn Marino
203e4b17023SJohn Marino old_size = ggc_get_size (x);
204e4b17023SJohn Marino
205e4b17023SJohn Marino if (size <= old_size)
206e4b17023SJohn Marino {
207e4b17023SJohn Marino /* Mark the unwanted memory as unaccessible. We also need to make
208e4b17023SJohn Marino the "new" size accessible, since ggc_get_size returns the size of
209e4b17023SJohn Marino the pool, not the size of the individually allocated object, the
210e4b17023SJohn Marino size which was previously made accessible. Unfortunately, we
211e4b17023SJohn Marino don't know that previously allocated size. Without that
212e4b17023SJohn Marino knowledge we have to lose some initialization-tracking for the
213e4b17023SJohn Marino old parts of the object. An alternative is to mark the whole
214e4b17023SJohn Marino old_size as reachable, but that would lose tracking of writes
215e4b17023SJohn Marino after the end of the object (by small offsets). Discard the
216e4b17023SJohn Marino handle to avoid handle leak. */
217e4b17023SJohn Marino VALGRIND_DISCARD (VALGRIND_MAKE_MEM_NOACCESS ((char *) x + size,
218e4b17023SJohn Marino old_size - size));
219e4b17023SJohn Marino VALGRIND_DISCARD (VALGRIND_MAKE_MEM_DEFINED (x, size));
220e4b17023SJohn Marino return x;
221e4b17023SJohn Marino }
222e4b17023SJohn Marino
223e4b17023SJohn Marino r = ggc_internal_alloc_stat (size PASS_MEM_STAT);
224e4b17023SJohn Marino
225e4b17023SJohn Marino /* Since ggc_get_size returns the size of the pool, not the size of the
226e4b17023SJohn Marino individually allocated object, we'd access parts of the old object
227e4b17023SJohn Marino that were marked invalid with the memcpy below. We lose a bit of the
228e4b17023SJohn Marino initialization-tracking since some of it may be uninitialized. */
229e4b17023SJohn Marino VALGRIND_DISCARD (VALGRIND_MAKE_MEM_DEFINED (x, old_size));
230e4b17023SJohn Marino
231e4b17023SJohn Marino memcpy (r, x, old_size);
232e4b17023SJohn Marino
233e4b17023SJohn Marino /* The old object is not supposed to be used anymore. */
234e4b17023SJohn Marino ggc_free (x);
235e4b17023SJohn Marino
236e4b17023SJohn Marino return r;
237e4b17023SJohn Marino }
238e4b17023SJohn Marino
239e4b17023SJohn Marino void *
ggc_cleared_alloc_htab_ignore_args(size_t c ATTRIBUTE_UNUSED,size_t n ATTRIBUTE_UNUSED)240e4b17023SJohn Marino ggc_cleared_alloc_htab_ignore_args (size_t c ATTRIBUTE_UNUSED,
241e4b17023SJohn Marino size_t n ATTRIBUTE_UNUSED)
242e4b17023SJohn Marino {
243e4b17023SJohn Marino gcc_assert (c * n == sizeof (struct htab));
244e4b17023SJohn Marino return ggc_alloc_cleared_htab ();
245e4b17023SJohn Marino }
246e4b17023SJohn Marino
247e4b17023SJohn Marino /* TODO: once we actually use type information in GGC, create a new tag
248e4b17023SJohn Marino gt_gcc_ptr_array and use it for pointer arrays. */
249e4b17023SJohn Marino void *
ggc_cleared_alloc_ptr_array_two_args(size_t c,size_t n)250e4b17023SJohn Marino ggc_cleared_alloc_ptr_array_two_args (size_t c, size_t n)
251e4b17023SJohn Marino {
252e4b17023SJohn Marino gcc_assert (sizeof (PTR *) == n);
253e4b17023SJohn Marino return ggc_internal_cleared_vec_alloc (sizeof (PTR *), c);
254e4b17023SJohn Marino }
255e4b17023SJohn Marino
256e4b17023SJohn Marino /* These are for splay_tree_new_ggc. */
257e4b17023SJohn Marino void *
ggc_splay_alloc(enum gt_types_enum obj_type ATTRIBUTE_UNUSED,int sz,void * nl)258e4b17023SJohn Marino ggc_splay_alloc (enum gt_types_enum obj_type ATTRIBUTE_UNUSED, int sz,
259e4b17023SJohn Marino void *nl)
260e4b17023SJohn Marino {
261e4b17023SJohn Marino gcc_assert (!nl);
262e4b17023SJohn Marino return ggc_internal_alloc (sz);
263e4b17023SJohn Marino }
264e4b17023SJohn Marino
265e4b17023SJohn Marino void
ggc_splay_dont_free(void * x ATTRIBUTE_UNUSED,void * nl)266e4b17023SJohn Marino ggc_splay_dont_free (void * x ATTRIBUTE_UNUSED, void *nl)
267e4b17023SJohn Marino {
268e4b17023SJohn Marino gcc_assert (!nl);
269e4b17023SJohn Marino }
270e4b17023SJohn Marino
271e4b17023SJohn Marino /* Print statistics that are independent of the collector in use. */
272e4b17023SJohn Marino #define SCALE(x) ((unsigned long) ((x) < 1024*10 \
273e4b17023SJohn Marino ? (x) \
274e4b17023SJohn Marino : ((x) < 1024*1024*10 \
275e4b17023SJohn Marino ? (x) / 1024 \
276e4b17023SJohn Marino : (x) / (1024*1024))))
277e4b17023SJohn Marino #define LABEL(x) ((x) < 1024*10 ? ' ' : ((x) < 1024*1024*10 ? 'k' : 'M'))
278e4b17023SJohn Marino
279e4b17023SJohn Marino void
ggc_print_common_statistics(FILE * stream ATTRIBUTE_UNUSED,ggc_statistics * stats)280e4b17023SJohn Marino ggc_print_common_statistics (FILE *stream ATTRIBUTE_UNUSED,
281e4b17023SJohn Marino ggc_statistics *stats)
282e4b17023SJohn Marino {
283e4b17023SJohn Marino /* Set the pointer so that during collection we will actually gather
284e4b17023SJohn Marino the statistics. */
285e4b17023SJohn Marino ggc_stats = stats;
286e4b17023SJohn Marino
287e4b17023SJohn Marino /* Then do one collection to fill in the statistics. */
288e4b17023SJohn Marino ggc_collect ();
289e4b17023SJohn Marino
290e4b17023SJohn Marino /* At present, we don't really gather any interesting statistics. */
291e4b17023SJohn Marino
292e4b17023SJohn Marino /* Don't gather statistics any more. */
293e4b17023SJohn Marino ggc_stats = NULL;
294e4b17023SJohn Marino }
295e4b17023SJohn Marino
296e4b17023SJohn Marino /* Functions for saving and restoring GCable memory to disk. */
297e4b17023SJohn Marino
298e4b17023SJohn Marino static htab_t saving_htab;
299e4b17023SJohn Marino
300e4b17023SJohn Marino struct ptr_data
301e4b17023SJohn Marino {
302e4b17023SJohn Marino void *obj;
303e4b17023SJohn Marino void *note_ptr_cookie;
304e4b17023SJohn Marino gt_note_pointers note_ptr_fn;
305e4b17023SJohn Marino gt_handle_reorder reorder_fn;
306e4b17023SJohn Marino size_t size;
307e4b17023SJohn Marino void *new_addr;
308e4b17023SJohn Marino enum gt_types_enum type;
309e4b17023SJohn Marino };
310e4b17023SJohn Marino
311*5ce9237cSJohn Marino #define POINTER_HASH(x) (hashval_t)((intptr_t)x >> 3)
312e4b17023SJohn Marino
313e4b17023SJohn Marino /* Register an object in the hash table. */
314e4b17023SJohn Marino
315e4b17023SJohn Marino int
gt_pch_note_object(void * obj,void * note_ptr_cookie,gt_note_pointers note_ptr_fn,enum gt_types_enum type)316e4b17023SJohn Marino gt_pch_note_object (void *obj, void *note_ptr_cookie,
317e4b17023SJohn Marino gt_note_pointers note_ptr_fn,
318e4b17023SJohn Marino enum gt_types_enum type)
319e4b17023SJohn Marino {
320e4b17023SJohn Marino struct ptr_data **slot;
321e4b17023SJohn Marino
322e4b17023SJohn Marino if (obj == NULL || obj == (void *) 1)
323e4b17023SJohn Marino return 0;
324e4b17023SJohn Marino
325e4b17023SJohn Marino slot = (struct ptr_data **)
326e4b17023SJohn Marino htab_find_slot_with_hash (saving_htab, obj, POINTER_HASH (obj),
327e4b17023SJohn Marino INSERT);
328e4b17023SJohn Marino if (*slot != NULL)
329e4b17023SJohn Marino {
330e4b17023SJohn Marino gcc_assert ((*slot)->note_ptr_fn == note_ptr_fn
331e4b17023SJohn Marino && (*slot)->note_ptr_cookie == note_ptr_cookie);
332e4b17023SJohn Marino return 0;
333e4b17023SJohn Marino }
334e4b17023SJohn Marino
335e4b17023SJohn Marino *slot = XCNEW (struct ptr_data);
336e4b17023SJohn Marino (*slot)->obj = obj;
337e4b17023SJohn Marino (*slot)->note_ptr_fn = note_ptr_fn;
338e4b17023SJohn Marino (*slot)->note_ptr_cookie = note_ptr_cookie;
339e4b17023SJohn Marino if (note_ptr_fn == gt_pch_p_S)
340e4b17023SJohn Marino (*slot)->size = strlen ((const char *)obj) + 1;
341e4b17023SJohn Marino else
342e4b17023SJohn Marino (*slot)->size = ggc_get_size (obj);
343e4b17023SJohn Marino (*slot)->type = type;
344e4b17023SJohn Marino return 1;
345e4b17023SJohn Marino }
346e4b17023SJohn Marino
347e4b17023SJohn Marino /* Register an object in the hash table. */
348e4b17023SJohn Marino
349e4b17023SJohn Marino void
gt_pch_note_reorder(void * obj,void * note_ptr_cookie,gt_handle_reorder reorder_fn)350e4b17023SJohn Marino gt_pch_note_reorder (void *obj, void *note_ptr_cookie,
351e4b17023SJohn Marino gt_handle_reorder reorder_fn)
352e4b17023SJohn Marino {
353e4b17023SJohn Marino struct ptr_data *data;
354e4b17023SJohn Marino
355e4b17023SJohn Marino if (obj == NULL || obj == (void *) 1)
356e4b17023SJohn Marino return;
357e4b17023SJohn Marino
358e4b17023SJohn Marino data = (struct ptr_data *)
359e4b17023SJohn Marino htab_find_with_hash (saving_htab, obj, POINTER_HASH (obj));
360e4b17023SJohn Marino gcc_assert (data && data->note_ptr_cookie == note_ptr_cookie);
361e4b17023SJohn Marino
362e4b17023SJohn Marino data->reorder_fn = reorder_fn;
363e4b17023SJohn Marino }
364e4b17023SJohn Marino
365e4b17023SJohn Marino /* Hash and equality functions for saving_htab, callbacks for htab_create. */
366e4b17023SJohn Marino
367e4b17023SJohn Marino static hashval_t
saving_htab_hash(const void * p)368e4b17023SJohn Marino saving_htab_hash (const void *p)
369e4b17023SJohn Marino {
370e4b17023SJohn Marino return POINTER_HASH (((const struct ptr_data *)p)->obj);
371e4b17023SJohn Marino }
372e4b17023SJohn Marino
373e4b17023SJohn Marino static int
saving_htab_eq(const void * p1,const void * p2)374e4b17023SJohn Marino saving_htab_eq (const void *p1, const void *p2)
375e4b17023SJohn Marino {
376e4b17023SJohn Marino return ((const struct ptr_data *)p1)->obj == p2;
377e4b17023SJohn Marino }
378e4b17023SJohn Marino
379e4b17023SJohn Marino /* Handy state for the traversal functions. */
380e4b17023SJohn Marino
381e4b17023SJohn Marino struct traversal_state
382e4b17023SJohn Marino {
383e4b17023SJohn Marino FILE *f;
384e4b17023SJohn Marino struct ggc_pch_data *d;
385e4b17023SJohn Marino size_t count;
386e4b17023SJohn Marino struct ptr_data **ptrs;
387e4b17023SJohn Marino size_t ptrs_i;
388e4b17023SJohn Marino };
389e4b17023SJohn Marino
390e4b17023SJohn Marino /* Callbacks for htab_traverse. */
391e4b17023SJohn Marino
392e4b17023SJohn Marino static int
call_count(void ** slot,void * state_p)393e4b17023SJohn Marino call_count (void **slot, void *state_p)
394e4b17023SJohn Marino {
395e4b17023SJohn Marino struct ptr_data *d = (struct ptr_data *)*slot;
396e4b17023SJohn Marino struct traversal_state *state = (struct traversal_state *)state_p;
397e4b17023SJohn Marino
398e4b17023SJohn Marino ggc_pch_count_object (state->d, d->obj, d->size,
399e4b17023SJohn Marino d->note_ptr_fn == gt_pch_p_S,
400e4b17023SJohn Marino d->type);
401e4b17023SJohn Marino state->count++;
402e4b17023SJohn Marino return 1;
403e4b17023SJohn Marino }
404e4b17023SJohn Marino
405e4b17023SJohn Marino static int
call_alloc(void ** slot,void * state_p)406e4b17023SJohn Marino call_alloc (void **slot, void *state_p)
407e4b17023SJohn Marino {
408e4b17023SJohn Marino struct ptr_data *d = (struct ptr_data *)*slot;
409e4b17023SJohn Marino struct traversal_state *state = (struct traversal_state *)state_p;
410e4b17023SJohn Marino
411e4b17023SJohn Marino d->new_addr = ggc_pch_alloc_object (state->d, d->obj, d->size,
412e4b17023SJohn Marino d->note_ptr_fn == gt_pch_p_S,
413e4b17023SJohn Marino d->type);
414e4b17023SJohn Marino state->ptrs[state->ptrs_i++] = d;
415e4b17023SJohn Marino return 1;
416e4b17023SJohn Marino }
417e4b17023SJohn Marino
418e4b17023SJohn Marino /* Callback for qsort. */
419e4b17023SJohn Marino
420e4b17023SJohn Marino static int
compare_ptr_data(const void * p1_p,const void * p2_p)421e4b17023SJohn Marino compare_ptr_data (const void *p1_p, const void *p2_p)
422e4b17023SJohn Marino {
423e4b17023SJohn Marino const struct ptr_data *const p1 = *(const struct ptr_data *const *)p1_p;
424e4b17023SJohn Marino const struct ptr_data *const p2 = *(const struct ptr_data *const *)p2_p;
425e4b17023SJohn Marino return (((size_t)p1->new_addr > (size_t)p2->new_addr)
426e4b17023SJohn Marino - ((size_t)p1->new_addr < (size_t)p2->new_addr));
427e4b17023SJohn Marino }
428e4b17023SJohn Marino
429e4b17023SJohn Marino /* Callbacks for note_ptr_fn. */
430e4b17023SJohn Marino
431e4b17023SJohn Marino static void
relocate_ptrs(void * ptr_p,void * state_p)432e4b17023SJohn Marino relocate_ptrs (void *ptr_p, void *state_p)
433e4b17023SJohn Marino {
434e4b17023SJohn Marino void **ptr = (void **)ptr_p;
435e4b17023SJohn Marino struct traversal_state *state ATTRIBUTE_UNUSED
436e4b17023SJohn Marino = (struct traversal_state *)state_p;
437e4b17023SJohn Marino struct ptr_data *result;
438e4b17023SJohn Marino
439e4b17023SJohn Marino if (*ptr == NULL || *ptr == (void *)1)
440e4b17023SJohn Marino return;
441e4b17023SJohn Marino
442e4b17023SJohn Marino result = (struct ptr_data *)
443e4b17023SJohn Marino htab_find_with_hash (saving_htab, *ptr, POINTER_HASH (*ptr));
444e4b17023SJohn Marino gcc_assert (result);
445e4b17023SJohn Marino *ptr = result->new_addr;
446e4b17023SJohn Marino }
447e4b17023SJohn Marino
448e4b17023SJohn Marino /* Write out, after relocation, the pointers in TAB. */
449e4b17023SJohn Marino static void
write_pch_globals(const struct ggc_root_tab * const * tab,struct traversal_state * state)450e4b17023SJohn Marino write_pch_globals (const struct ggc_root_tab * const *tab,
451e4b17023SJohn Marino struct traversal_state *state)
452e4b17023SJohn Marino {
453e4b17023SJohn Marino const struct ggc_root_tab *const *rt;
454e4b17023SJohn Marino const struct ggc_root_tab *rti;
455e4b17023SJohn Marino size_t i;
456e4b17023SJohn Marino
457e4b17023SJohn Marino for (rt = tab; *rt; rt++)
458e4b17023SJohn Marino for (rti = *rt; rti->base != NULL; rti++)
459e4b17023SJohn Marino for (i = 0; i < rti->nelt; i++)
460e4b17023SJohn Marino {
461e4b17023SJohn Marino void *ptr = *(void **)((char *)rti->base + rti->stride * i);
462e4b17023SJohn Marino struct ptr_data *new_ptr;
463e4b17023SJohn Marino if (ptr == NULL || ptr == (void *)1)
464e4b17023SJohn Marino {
465e4b17023SJohn Marino if (fwrite (&ptr, sizeof (void *), 1, state->f)
466e4b17023SJohn Marino != 1)
467e4b17023SJohn Marino fatal_error ("can%'t write PCH file: %m");
468e4b17023SJohn Marino }
469e4b17023SJohn Marino else
470e4b17023SJohn Marino {
471e4b17023SJohn Marino new_ptr = (struct ptr_data *)
472e4b17023SJohn Marino htab_find_with_hash (saving_htab, ptr, POINTER_HASH (ptr));
473e4b17023SJohn Marino if (fwrite (&new_ptr->new_addr, sizeof (void *), 1, state->f)
474e4b17023SJohn Marino != 1)
475e4b17023SJohn Marino fatal_error ("can%'t write PCH file: %m");
476e4b17023SJohn Marino }
477e4b17023SJohn Marino }
478e4b17023SJohn Marino }
479e4b17023SJohn Marino
480e4b17023SJohn Marino /* Hold the information we need to mmap the file back in. */
481e4b17023SJohn Marino
482e4b17023SJohn Marino struct mmap_info
483e4b17023SJohn Marino {
484e4b17023SJohn Marino size_t offset;
485e4b17023SJohn Marino size_t size;
486e4b17023SJohn Marino void *preferred_base;
487e4b17023SJohn Marino };
488e4b17023SJohn Marino
489e4b17023SJohn Marino /* Write out the state of the compiler to F. */
490e4b17023SJohn Marino
491e4b17023SJohn Marino void
gt_pch_save(FILE * f)492e4b17023SJohn Marino gt_pch_save (FILE *f)
493e4b17023SJohn Marino {
494e4b17023SJohn Marino const struct ggc_root_tab *const *rt;
495e4b17023SJohn Marino const struct ggc_root_tab *rti;
496e4b17023SJohn Marino size_t i;
497e4b17023SJohn Marino struct traversal_state state;
498e4b17023SJohn Marino char *this_object = NULL;
499e4b17023SJohn Marino size_t this_object_size = 0;
500e4b17023SJohn Marino struct mmap_info mmi;
501e4b17023SJohn Marino const size_t mmap_offset_alignment = host_hooks.gt_pch_alloc_granularity();
502e4b17023SJohn Marino
503e4b17023SJohn Marino gt_pch_save_stringpool ();
504e4b17023SJohn Marino
505e4b17023SJohn Marino timevar_push (TV_PCH_PTR_REALLOC);
506e4b17023SJohn Marino saving_htab = htab_create (50000, saving_htab_hash, saving_htab_eq, free);
507e4b17023SJohn Marino
508e4b17023SJohn Marino for (rt = gt_ggc_rtab; *rt; rt++)
509e4b17023SJohn Marino for (rti = *rt; rti->base != NULL; rti++)
510e4b17023SJohn Marino for (i = 0; i < rti->nelt; i++)
511e4b17023SJohn Marino (*rti->pchw)(*(void **)((char *)rti->base + rti->stride * i));
512e4b17023SJohn Marino
513e4b17023SJohn Marino for (rt = gt_pch_cache_rtab; *rt; rt++)
514e4b17023SJohn Marino for (rti = *rt; rti->base != NULL; rti++)
515e4b17023SJohn Marino for (i = 0; i < rti->nelt; i++)
516e4b17023SJohn Marino (*rti->pchw)(*(void **)((char *)rti->base + rti->stride * i));
517e4b17023SJohn Marino
518e4b17023SJohn Marino /* Prepare the objects for writing, determine addresses and such. */
519e4b17023SJohn Marino state.f = f;
520e4b17023SJohn Marino state.d = init_ggc_pch ();
521e4b17023SJohn Marino state.count = 0;
522e4b17023SJohn Marino htab_traverse (saving_htab, call_count, &state);
523e4b17023SJohn Marino
524e4b17023SJohn Marino mmi.size = ggc_pch_total_size (state.d);
525e4b17023SJohn Marino
526e4b17023SJohn Marino /* Try to arrange things so that no relocation is necessary, but
527e4b17023SJohn Marino don't try very hard. On most platforms, this will always work,
528e4b17023SJohn Marino and on the rest it's a lot of work to do better.
529e4b17023SJohn Marino (The extra work goes in HOST_HOOKS_GT_PCH_GET_ADDRESS and
530e4b17023SJohn Marino HOST_HOOKS_GT_PCH_USE_ADDRESS.) */
531e4b17023SJohn Marino mmi.preferred_base = host_hooks.gt_pch_get_address (mmi.size, fileno (f));
532e4b17023SJohn Marino
533e4b17023SJohn Marino ggc_pch_this_base (state.d, mmi.preferred_base);
534e4b17023SJohn Marino
535e4b17023SJohn Marino state.ptrs = XNEWVEC (struct ptr_data *, state.count);
536e4b17023SJohn Marino state.ptrs_i = 0;
537e4b17023SJohn Marino
538e4b17023SJohn Marino htab_traverse (saving_htab, call_alloc, &state);
539e4b17023SJohn Marino timevar_pop (TV_PCH_PTR_REALLOC);
540e4b17023SJohn Marino
541e4b17023SJohn Marino timevar_push (TV_PCH_PTR_SORT);
542e4b17023SJohn Marino qsort (state.ptrs, state.count, sizeof (*state.ptrs), compare_ptr_data);
543e4b17023SJohn Marino timevar_pop (TV_PCH_PTR_SORT);
544e4b17023SJohn Marino
545e4b17023SJohn Marino /* Write out all the scalar variables. */
546e4b17023SJohn Marino for (rt = gt_pch_scalar_rtab; *rt; rt++)
547e4b17023SJohn Marino for (rti = *rt; rti->base != NULL; rti++)
548e4b17023SJohn Marino if (fwrite (rti->base, rti->stride, 1, f) != 1)
549e4b17023SJohn Marino fatal_error ("can%'t write PCH file: %m");
550e4b17023SJohn Marino
551e4b17023SJohn Marino /* Write out all the global pointers, after translation. */
552e4b17023SJohn Marino write_pch_globals (gt_ggc_rtab, &state);
553e4b17023SJohn Marino write_pch_globals (gt_pch_cache_rtab, &state);
554e4b17023SJohn Marino
555e4b17023SJohn Marino /* Pad the PCH file so that the mmapped area starts on an allocation
556e4b17023SJohn Marino granularity (usually page) boundary. */
557e4b17023SJohn Marino {
558e4b17023SJohn Marino long o;
559e4b17023SJohn Marino o = ftell (state.f) + sizeof (mmi);
560e4b17023SJohn Marino if (o == -1)
561e4b17023SJohn Marino fatal_error ("can%'t get position in PCH file: %m");
562e4b17023SJohn Marino mmi.offset = mmap_offset_alignment - o % mmap_offset_alignment;
563e4b17023SJohn Marino if (mmi.offset == mmap_offset_alignment)
564e4b17023SJohn Marino mmi.offset = 0;
565e4b17023SJohn Marino mmi.offset += o;
566e4b17023SJohn Marino }
567e4b17023SJohn Marino if (fwrite (&mmi, sizeof (mmi), 1, state.f) != 1)
568e4b17023SJohn Marino fatal_error ("can%'t write PCH file: %m");
569e4b17023SJohn Marino if (mmi.offset != 0
570e4b17023SJohn Marino && fseek (state.f, mmi.offset, SEEK_SET) != 0)
571e4b17023SJohn Marino fatal_error ("can%'t write padding to PCH file: %m");
572e4b17023SJohn Marino
573e4b17023SJohn Marino ggc_pch_prepare_write (state.d, state.f);
574e4b17023SJohn Marino
575e4b17023SJohn Marino /* Actually write out the objects. */
576e4b17023SJohn Marino for (i = 0; i < state.count; i++)
577e4b17023SJohn Marino {
578e4b17023SJohn Marino if (this_object_size < state.ptrs[i]->size)
579e4b17023SJohn Marino {
580e4b17023SJohn Marino this_object_size = state.ptrs[i]->size;
581e4b17023SJohn Marino this_object = XRESIZEVAR (char, this_object, this_object_size);
582e4b17023SJohn Marino }
583e4b17023SJohn Marino memcpy (this_object, state.ptrs[i]->obj, state.ptrs[i]->size);
584e4b17023SJohn Marino if (state.ptrs[i]->reorder_fn != NULL)
585e4b17023SJohn Marino state.ptrs[i]->reorder_fn (state.ptrs[i]->obj,
586e4b17023SJohn Marino state.ptrs[i]->note_ptr_cookie,
587e4b17023SJohn Marino relocate_ptrs, &state);
588e4b17023SJohn Marino state.ptrs[i]->note_ptr_fn (state.ptrs[i]->obj,
589e4b17023SJohn Marino state.ptrs[i]->note_ptr_cookie,
590e4b17023SJohn Marino relocate_ptrs, &state);
591e4b17023SJohn Marino ggc_pch_write_object (state.d, state.f, state.ptrs[i]->obj,
592e4b17023SJohn Marino state.ptrs[i]->new_addr, state.ptrs[i]->size,
593e4b17023SJohn Marino state.ptrs[i]->note_ptr_fn == gt_pch_p_S);
594e4b17023SJohn Marino if (state.ptrs[i]->note_ptr_fn != gt_pch_p_S)
595e4b17023SJohn Marino memcpy (state.ptrs[i]->obj, this_object, state.ptrs[i]->size);
596e4b17023SJohn Marino }
597e4b17023SJohn Marino ggc_pch_finish (state.d, state.f);
598e4b17023SJohn Marino gt_pch_fixup_stringpool ();
599e4b17023SJohn Marino
600e4b17023SJohn Marino free (state.ptrs);
601e4b17023SJohn Marino htab_delete (saving_htab);
602e4b17023SJohn Marino }
603e4b17023SJohn Marino
604e4b17023SJohn Marino /* Read the state of the compiler back in from F. */
605e4b17023SJohn Marino
606e4b17023SJohn Marino void
gt_pch_restore(FILE * f)607e4b17023SJohn Marino gt_pch_restore (FILE *f)
608e4b17023SJohn Marino {
609e4b17023SJohn Marino const struct ggc_root_tab *const *rt;
610e4b17023SJohn Marino const struct ggc_root_tab *rti;
611e4b17023SJohn Marino size_t i;
612e4b17023SJohn Marino struct mmap_info mmi;
613e4b17023SJohn Marino int result;
614e4b17023SJohn Marino
615e4b17023SJohn Marino /* Delete any deletable objects. This makes ggc_pch_read much
616e4b17023SJohn Marino faster, as it can be sure that no GCable objects remain other
617e4b17023SJohn Marino than the ones just read in. */
618e4b17023SJohn Marino for (rt = gt_ggc_deletable_rtab; *rt; rt++)
619e4b17023SJohn Marino for (rti = *rt; rti->base != NULL; rti++)
620e4b17023SJohn Marino memset (rti->base, 0, rti->stride);
621e4b17023SJohn Marino
622e4b17023SJohn Marino /* Read in all the scalar variables. */
623e4b17023SJohn Marino for (rt = gt_pch_scalar_rtab; *rt; rt++)
624e4b17023SJohn Marino for (rti = *rt; rti->base != NULL; rti++)
625e4b17023SJohn Marino if (fread (rti->base, rti->stride, 1, f) != 1)
626e4b17023SJohn Marino fatal_error ("can%'t read PCH file: %m");
627e4b17023SJohn Marino
628e4b17023SJohn Marino /* Read in all the global pointers, in 6 easy loops. */
629e4b17023SJohn Marino for (rt = gt_ggc_rtab; *rt; rt++)
630e4b17023SJohn Marino for (rti = *rt; rti->base != NULL; rti++)
631e4b17023SJohn Marino for (i = 0; i < rti->nelt; i++)
632e4b17023SJohn Marino if (fread ((char *)rti->base + rti->stride * i,
633e4b17023SJohn Marino sizeof (void *), 1, f) != 1)
634e4b17023SJohn Marino fatal_error ("can%'t read PCH file: %m");
635e4b17023SJohn Marino
636e4b17023SJohn Marino for (rt = gt_pch_cache_rtab; *rt; rt++)
637e4b17023SJohn Marino for (rti = *rt; rti->base != NULL; rti++)
638e4b17023SJohn Marino for (i = 0; i < rti->nelt; i++)
639e4b17023SJohn Marino if (fread ((char *)rti->base + rti->stride * i,
640e4b17023SJohn Marino sizeof (void *), 1, f) != 1)
641e4b17023SJohn Marino fatal_error ("can%'t read PCH file: %m");
642e4b17023SJohn Marino
643e4b17023SJohn Marino if (fread (&mmi, sizeof (mmi), 1, f) != 1)
644e4b17023SJohn Marino fatal_error ("can%'t read PCH file: %m");
645e4b17023SJohn Marino
646e4b17023SJohn Marino result = host_hooks.gt_pch_use_address (mmi.preferred_base, mmi.size,
647e4b17023SJohn Marino fileno (f), mmi.offset);
648e4b17023SJohn Marino if (result < 0)
649e4b17023SJohn Marino fatal_error ("had to relocate PCH");
650e4b17023SJohn Marino if (result == 0)
651e4b17023SJohn Marino {
652e4b17023SJohn Marino if (fseek (f, mmi.offset, SEEK_SET) != 0
653e4b17023SJohn Marino || fread (mmi.preferred_base, mmi.size, 1, f) != 1)
654e4b17023SJohn Marino fatal_error ("can%'t read PCH file: %m");
655e4b17023SJohn Marino }
656e4b17023SJohn Marino else if (fseek (f, mmi.offset + mmi.size, SEEK_SET) != 0)
657e4b17023SJohn Marino fatal_error ("can%'t read PCH file: %m");
658e4b17023SJohn Marino
659e4b17023SJohn Marino ggc_pch_read (f, mmi.preferred_base);
660e4b17023SJohn Marino
661e4b17023SJohn Marino gt_pch_restore_stringpool ();
662e4b17023SJohn Marino }
663e4b17023SJohn Marino
664e4b17023SJohn Marino /* Default version of HOST_HOOKS_GT_PCH_GET_ADDRESS when mmap is not present.
665e4b17023SJohn Marino Select no address whatsoever, and let gt_pch_save choose what it will with
666e4b17023SJohn Marino malloc, presumably. */
667e4b17023SJohn Marino
668e4b17023SJohn Marino void *
default_gt_pch_get_address(size_t size ATTRIBUTE_UNUSED,int fd ATTRIBUTE_UNUSED)669e4b17023SJohn Marino default_gt_pch_get_address (size_t size ATTRIBUTE_UNUSED,
670e4b17023SJohn Marino int fd ATTRIBUTE_UNUSED)
671e4b17023SJohn Marino {
672e4b17023SJohn Marino return NULL;
673e4b17023SJohn Marino }
674e4b17023SJohn Marino
675e4b17023SJohn Marino /* Default version of HOST_HOOKS_GT_PCH_USE_ADDRESS when mmap is not present.
676e4b17023SJohn Marino Allocate SIZE bytes with malloc. Return 0 if the address we got is the
677e4b17023SJohn Marino same as base, indicating that the memory has been allocated but needs to
678e4b17023SJohn Marino be read in from the file. Return -1 if the address differs, to relocation
679e4b17023SJohn Marino of the PCH file would be required. */
680e4b17023SJohn Marino
681e4b17023SJohn Marino int
default_gt_pch_use_address(void * base,size_t size,int fd ATTRIBUTE_UNUSED,size_t offset ATTRIBUTE_UNUSED)682e4b17023SJohn Marino default_gt_pch_use_address (void *base, size_t size, int fd ATTRIBUTE_UNUSED,
683e4b17023SJohn Marino size_t offset ATTRIBUTE_UNUSED)
684e4b17023SJohn Marino {
685e4b17023SJohn Marino void *addr = xmalloc (size);
686e4b17023SJohn Marino return (addr == base) - 1;
687e4b17023SJohn Marino }
688e4b17023SJohn Marino
689e4b17023SJohn Marino /* Default version of HOST_HOOKS_GT_PCH_GET_ADDRESS. Return the
690e4b17023SJohn Marino alignment required for allocating virtual memory. Usually this is the
691e4b17023SJohn Marino same as pagesize. */
692e4b17023SJohn Marino
693e4b17023SJohn Marino size_t
default_gt_pch_alloc_granularity(void)694e4b17023SJohn Marino default_gt_pch_alloc_granularity (void)
695e4b17023SJohn Marino {
696e4b17023SJohn Marino return getpagesize();
697e4b17023SJohn Marino }
698e4b17023SJohn Marino
699e4b17023SJohn Marino #if HAVE_MMAP_FILE
700e4b17023SJohn Marino /* Default version of HOST_HOOKS_GT_PCH_GET_ADDRESS when mmap is present.
701e4b17023SJohn Marino We temporarily allocate SIZE bytes, and let the kernel place the data
702e4b17023SJohn Marino wherever it will. If it worked, that's our spot, if not we're likely
703e4b17023SJohn Marino to be in trouble. */
704e4b17023SJohn Marino
705e4b17023SJohn Marino void *
mmap_gt_pch_get_address(size_t size,int fd)706e4b17023SJohn Marino mmap_gt_pch_get_address (size_t size, int fd)
707e4b17023SJohn Marino {
708e4b17023SJohn Marino void *ret;
709e4b17023SJohn Marino
710e4b17023SJohn Marino ret = mmap (NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
711e4b17023SJohn Marino if (ret == (void *) MAP_FAILED)
712e4b17023SJohn Marino ret = NULL;
713e4b17023SJohn Marino else
714e4b17023SJohn Marino munmap ((caddr_t) ret, size);
715e4b17023SJohn Marino
716e4b17023SJohn Marino return ret;
717e4b17023SJohn Marino }
718e4b17023SJohn Marino
719e4b17023SJohn Marino /* Default version of HOST_HOOKS_GT_PCH_USE_ADDRESS when mmap is present.
720e4b17023SJohn Marino Map SIZE bytes of FD+OFFSET at BASE. Return 1 if we succeeded at
721e4b17023SJohn Marino mapping the data at BASE, -1 if we couldn't.
722e4b17023SJohn Marino
723e4b17023SJohn Marino This version assumes that the kernel honors the START operand of mmap
724e4b17023SJohn Marino even without MAP_FIXED if START through START+SIZE are not currently
725e4b17023SJohn Marino mapped with something. */
726e4b17023SJohn Marino
727e4b17023SJohn Marino int
mmap_gt_pch_use_address(void * base,size_t size,int fd,size_t offset)728e4b17023SJohn Marino mmap_gt_pch_use_address (void *base, size_t size, int fd, size_t offset)
729e4b17023SJohn Marino {
730e4b17023SJohn Marino void *addr;
731e4b17023SJohn Marino
732e4b17023SJohn Marino /* We're called with size == 0 if we're not planning to load a PCH
733e4b17023SJohn Marino file at all. This allows the hook to free any static space that
734e4b17023SJohn Marino we might have allocated at link time. */
735e4b17023SJohn Marino if (size == 0)
736e4b17023SJohn Marino return -1;
737e4b17023SJohn Marino
738e4b17023SJohn Marino addr = mmap ((caddr_t) base, size, PROT_READ | PROT_WRITE, MAP_PRIVATE,
739e4b17023SJohn Marino fd, offset);
740e4b17023SJohn Marino
741e4b17023SJohn Marino return addr == base ? 1 : -1;
742e4b17023SJohn Marino }
743e4b17023SJohn Marino #endif /* HAVE_MMAP_FILE */
744e4b17023SJohn Marino
745e4b17023SJohn Marino #if !defined ENABLE_GC_CHECKING && !defined ENABLE_GC_ALWAYS_COLLECT
746e4b17023SJohn Marino
747e4b17023SJohn Marino /* Modify the bound based on rlimits. */
748e4b17023SJohn Marino static double
ggc_rlimit_bound(double limit)749e4b17023SJohn Marino ggc_rlimit_bound (double limit)
750e4b17023SJohn Marino {
751e4b17023SJohn Marino #if defined(HAVE_GETRLIMIT)
752e4b17023SJohn Marino struct rlimit rlim;
753e4b17023SJohn Marino # if defined (RLIMIT_AS)
754e4b17023SJohn Marino /* RLIMIT_AS is what POSIX says is the limit on mmap. Presumably
755e4b17023SJohn Marino any OS which has RLIMIT_AS also has a working mmap that GCC will use. */
756e4b17023SJohn Marino if (getrlimit (RLIMIT_AS, &rlim) == 0
757e4b17023SJohn Marino && rlim.rlim_cur != (rlim_t) RLIM_INFINITY
758e4b17023SJohn Marino && rlim.rlim_cur < limit)
759e4b17023SJohn Marino limit = rlim.rlim_cur;
760e4b17023SJohn Marino # elif defined (RLIMIT_DATA)
761e4b17023SJohn Marino /* ... but some older OSs bound mmap based on RLIMIT_DATA, or we
762e4b17023SJohn Marino might be on an OS that has a broken mmap. (Others don't bound
763e4b17023SJohn Marino mmap at all, apparently.) */
764e4b17023SJohn Marino if (getrlimit (RLIMIT_DATA, &rlim) == 0
765e4b17023SJohn Marino && rlim.rlim_cur != (rlim_t) RLIM_INFINITY
766e4b17023SJohn Marino && rlim.rlim_cur < limit
767e4b17023SJohn Marino /* Darwin has this horribly bogus default setting of
768e4b17023SJohn Marino RLIMIT_DATA, to 6144Kb. No-one notices because RLIMIT_DATA
769e4b17023SJohn Marino appears to be ignored. Ignore such silliness. If a limit
770e4b17023SJohn Marino this small was actually effective for mmap, GCC wouldn't even
771e4b17023SJohn Marino start up. */
772e4b17023SJohn Marino && rlim.rlim_cur >= 8 * 1024 * 1024)
773e4b17023SJohn Marino limit = rlim.rlim_cur;
774e4b17023SJohn Marino # endif /* RLIMIT_AS or RLIMIT_DATA */
775e4b17023SJohn Marino #endif /* HAVE_GETRLIMIT */
776e4b17023SJohn Marino
777e4b17023SJohn Marino return limit;
778e4b17023SJohn Marino }
779e4b17023SJohn Marino
780e4b17023SJohn Marino /* Heuristic to set a default for GGC_MIN_EXPAND. */
781e4b17023SJohn Marino static int
ggc_min_expand_heuristic(void)782e4b17023SJohn Marino ggc_min_expand_heuristic (void)
783e4b17023SJohn Marino {
784e4b17023SJohn Marino double min_expand = physmem_total();
785e4b17023SJohn Marino
786e4b17023SJohn Marino /* Adjust for rlimits. */
787e4b17023SJohn Marino min_expand = ggc_rlimit_bound (min_expand);
788e4b17023SJohn Marino
789e4b17023SJohn Marino /* The heuristic is a percentage equal to 30% + 70%*(RAM/1GB), yielding
790e4b17023SJohn Marino a lower bound of 30% and an upper bound of 100% (when RAM >= 1GB). */
791e4b17023SJohn Marino min_expand /= 1024*1024*1024;
792e4b17023SJohn Marino min_expand *= 70;
793e4b17023SJohn Marino min_expand = MIN (min_expand, 70);
794e4b17023SJohn Marino min_expand += 30;
795e4b17023SJohn Marino
796e4b17023SJohn Marino return min_expand;
797e4b17023SJohn Marino }
798e4b17023SJohn Marino
799e4b17023SJohn Marino /* Heuristic to set a default for GGC_MIN_HEAPSIZE. */
800e4b17023SJohn Marino static int
ggc_min_heapsize_heuristic(void)801e4b17023SJohn Marino ggc_min_heapsize_heuristic (void)
802e4b17023SJohn Marino {
803e4b17023SJohn Marino double phys_kbytes = physmem_total();
804e4b17023SJohn Marino double limit_kbytes = ggc_rlimit_bound (phys_kbytes * 2);
805e4b17023SJohn Marino
806e4b17023SJohn Marino phys_kbytes /= 1024; /* Convert to Kbytes. */
807e4b17023SJohn Marino limit_kbytes /= 1024;
808e4b17023SJohn Marino
809e4b17023SJohn Marino /* The heuristic is RAM/8, with a lower bound of 4M and an upper
810e4b17023SJohn Marino bound of 128M (when RAM >= 1GB). */
811e4b17023SJohn Marino phys_kbytes /= 8;
812e4b17023SJohn Marino
813e4b17023SJohn Marino #if defined(HAVE_GETRLIMIT) && defined (RLIMIT_RSS)
814e4b17023SJohn Marino /* Try not to overrun the RSS limit while doing garbage collection.
815e4b17023SJohn Marino The RSS limit is only advisory, so no margin is subtracted. */
816e4b17023SJohn Marino {
817e4b17023SJohn Marino struct rlimit rlim;
818e4b17023SJohn Marino if (getrlimit (RLIMIT_RSS, &rlim) == 0
819e4b17023SJohn Marino && rlim.rlim_cur != (rlim_t) RLIM_INFINITY)
820e4b17023SJohn Marino phys_kbytes = MIN (phys_kbytes, rlim.rlim_cur / 1024);
821e4b17023SJohn Marino }
822e4b17023SJohn Marino # endif
823e4b17023SJohn Marino
824e4b17023SJohn Marino /* Don't blindly run over our data limit; do GC at least when the
825e4b17023SJohn Marino *next* GC would be within 20Mb of the limit or within a quarter of
826e4b17023SJohn Marino the limit, whichever is larger. If GCC does hit the data limit,
827e4b17023SJohn Marino compilation will fail, so this tries to be conservative. */
828e4b17023SJohn Marino limit_kbytes = MAX (0, limit_kbytes - MAX (limit_kbytes / 4, 20 * 1024));
829e4b17023SJohn Marino limit_kbytes = (limit_kbytes * 100) / (110 + ggc_min_expand_heuristic ());
830e4b17023SJohn Marino phys_kbytes = MIN (phys_kbytes, limit_kbytes);
831e4b17023SJohn Marino
832e4b17023SJohn Marino phys_kbytes = MAX (phys_kbytes, 4 * 1024);
833e4b17023SJohn Marino phys_kbytes = MIN (phys_kbytes, 128 * 1024);
834e4b17023SJohn Marino
835e4b17023SJohn Marino return phys_kbytes;
836e4b17023SJohn Marino }
837e4b17023SJohn Marino #endif
838e4b17023SJohn Marino
839e4b17023SJohn Marino void
init_ggc_heuristics(void)840e4b17023SJohn Marino init_ggc_heuristics (void)
841e4b17023SJohn Marino {
842e4b17023SJohn Marino #if !defined ENABLE_GC_CHECKING && !defined ENABLE_GC_ALWAYS_COLLECT
843e4b17023SJohn Marino set_default_param_value (GGC_MIN_EXPAND, ggc_min_expand_heuristic ());
844e4b17023SJohn Marino set_default_param_value (GGC_MIN_HEAPSIZE, ggc_min_heapsize_heuristic ());
845e4b17023SJohn Marino #endif
846e4b17023SJohn Marino }
847e4b17023SJohn Marino
848e4b17023SJohn Marino #ifdef GATHER_STATISTICS
849e4b17023SJohn Marino
850e4b17023SJohn Marino /* Datastructure used to store per-call-site statistics. */
851e4b17023SJohn Marino struct loc_descriptor
852e4b17023SJohn Marino {
853e4b17023SJohn Marino const char *file;
854e4b17023SJohn Marino int line;
855e4b17023SJohn Marino const char *function;
856e4b17023SJohn Marino int times;
857e4b17023SJohn Marino size_t allocated;
858e4b17023SJohn Marino size_t overhead;
859e4b17023SJohn Marino size_t freed;
860e4b17023SJohn Marino size_t collected;
861e4b17023SJohn Marino };
862e4b17023SJohn Marino
863e4b17023SJohn Marino /* Hashtable used for statistics. */
864e4b17023SJohn Marino static htab_t loc_hash;
865e4b17023SJohn Marino
866e4b17023SJohn Marino /* Hash table helpers functions. */
867e4b17023SJohn Marino static hashval_t
hash_descriptor(const void * p)868e4b17023SJohn Marino hash_descriptor (const void *p)
869e4b17023SJohn Marino {
870e4b17023SJohn Marino const struct loc_descriptor *const d = (const struct loc_descriptor *) p;
871e4b17023SJohn Marino
872e4b17023SJohn Marino return htab_hash_pointer (d->function) | d->line;
873e4b17023SJohn Marino }
874e4b17023SJohn Marino
875e4b17023SJohn Marino static int
eq_descriptor(const void * p1,const void * p2)876e4b17023SJohn Marino eq_descriptor (const void *p1, const void *p2)
877e4b17023SJohn Marino {
878e4b17023SJohn Marino const struct loc_descriptor *const d = (const struct loc_descriptor *) p1;
879e4b17023SJohn Marino const struct loc_descriptor *const d2 = (const struct loc_descriptor *) p2;
880e4b17023SJohn Marino
881e4b17023SJohn Marino return (d->file == d2->file && d->line == d2->line
882e4b17023SJohn Marino && d->function == d2->function);
883e4b17023SJohn Marino }
884e4b17023SJohn Marino
885e4b17023SJohn Marino /* Hashtable converting address of allocated field to loc descriptor. */
886e4b17023SJohn Marino static htab_t ptr_hash;
887e4b17023SJohn Marino struct ptr_hash_entry
888e4b17023SJohn Marino {
889e4b17023SJohn Marino void *ptr;
890e4b17023SJohn Marino struct loc_descriptor *loc;
891e4b17023SJohn Marino size_t size;
892e4b17023SJohn Marino };
893e4b17023SJohn Marino
894e4b17023SJohn Marino /* Hash table helpers functions. */
895e4b17023SJohn Marino static hashval_t
hash_ptr(const void * p)896e4b17023SJohn Marino hash_ptr (const void *p)
897e4b17023SJohn Marino {
898e4b17023SJohn Marino const struct ptr_hash_entry *const d = (const struct ptr_hash_entry *) p;
899e4b17023SJohn Marino
900e4b17023SJohn Marino return htab_hash_pointer (d->ptr);
901e4b17023SJohn Marino }
902e4b17023SJohn Marino
903e4b17023SJohn Marino static int
eq_ptr(const void * p1,const void * p2)904e4b17023SJohn Marino eq_ptr (const void *p1, const void *p2)
905e4b17023SJohn Marino {
906e4b17023SJohn Marino const struct ptr_hash_entry *const p = (const struct ptr_hash_entry *) p1;
907e4b17023SJohn Marino
908e4b17023SJohn Marino return (p->ptr == p2);
909e4b17023SJohn Marino }
910e4b17023SJohn Marino
911e4b17023SJohn Marino /* Return descriptor for given call site, create new one if needed. */
912e4b17023SJohn Marino static struct loc_descriptor *
loc_descriptor(const char * name,int line,const char * function)913e4b17023SJohn Marino loc_descriptor (const char *name, int line, const char *function)
914e4b17023SJohn Marino {
915e4b17023SJohn Marino struct loc_descriptor loc;
916e4b17023SJohn Marino struct loc_descriptor **slot;
917e4b17023SJohn Marino
918e4b17023SJohn Marino loc.file = name;
919e4b17023SJohn Marino loc.line = line;
920e4b17023SJohn Marino loc.function = function;
921e4b17023SJohn Marino if (!loc_hash)
922e4b17023SJohn Marino loc_hash = htab_create (10, hash_descriptor, eq_descriptor, NULL);
923e4b17023SJohn Marino
924e4b17023SJohn Marino slot = (struct loc_descriptor **) htab_find_slot (loc_hash, &loc, INSERT);
925e4b17023SJohn Marino if (*slot)
926e4b17023SJohn Marino return *slot;
927e4b17023SJohn Marino *slot = XCNEW (struct loc_descriptor);
928e4b17023SJohn Marino (*slot)->file = name;
929e4b17023SJohn Marino (*slot)->line = line;
930e4b17023SJohn Marino (*slot)->function = function;
931e4b17023SJohn Marino return *slot;
932e4b17023SJohn Marino }
933e4b17023SJohn Marino
934e4b17023SJohn Marino /* Record ALLOCATED and OVERHEAD bytes to descriptor NAME:LINE (FUNCTION). */
935e4b17023SJohn Marino void
ggc_record_overhead(size_t allocated,size_t overhead,void * ptr,const char * name,int line,const char * function)936e4b17023SJohn Marino ggc_record_overhead (size_t allocated, size_t overhead, void *ptr,
937e4b17023SJohn Marino const char *name, int line, const char *function)
938e4b17023SJohn Marino {
939e4b17023SJohn Marino struct loc_descriptor *loc = loc_descriptor (name, line, function);
940e4b17023SJohn Marino struct ptr_hash_entry *p = XNEW (struct ptr_hash_entry);
941e4b17023SJohn Marino PTR *slot;
942e4b17023SJohn Marino
943e4b17023SJohn Marino p->ptr = ptr;
944e4b17023SJohn Marino p->loc = loc;
945e4b17023SJohn Marino p->size = allocated + overhead;
946e4b17023SJohn Marino if (!ptr_hash)
947e4b17023SJohn Marino ptr_hash = htab_create (10, hash_ptr, eq_ptr, NULL);
948e4b17023SJohn Marino slot = htab_find_slot_with_hash (ptr_hash, ptr, htab_hash_pointer (ptr), INSERT);
949e4b17023SJohn Marino gcc_assert (!*slot);
950e4b17023SJohn Marino *slot = p;
951e4b17023SJohn Marino
952e4b17023SJohn Marino loc->times++;
953e4b17023SJohn Marino loc->allocated+=allocated;
954e4b17023SJohn Marino loc->overhead+=overhead;
955e4b17023SJohn Marino }
956e4b17023SJohn Marino
957e4b17023SJohn Marino /* Helper function for prune_overhead_list. See if SLOT is still marked and
958e4b17023SJohn Marino remove it from hashtable if it is not. */
959e4b17023SJohn Marino static int
ggc_prune_ptr(void ** slot,void * b ATTRIBUTE_UNUSED)960e4b17023SJohn Marino ggc_prune_ptr (void **slot, void *b ATTRIBUTE_UNUSED)
961e4b17023SJohn Marino {
962e4b17023SJohn Marino struct ptr_hash_entry *p = (struct ptr_hash_entry *) *slot;
963e4b17023SJohn Marino if (!ggc_marked_p (p->ptr))
964e4b17023SJohn Marino {
965e4b17023SJohn Marino p->loc->collected += p->size;
966e4b17023SJohn Marino htab_clear_slot (ptr_hash, slot);
967e4b17023SJohn Marino free (p);
968e4b17023SJohn Marino }
969e4b17023SJohn Marino return 1;
970e4b17023SJohn Marino }
971e4b17023SJohn Marino
972e4b17023SJohn Marino /* After live values has been marked, walk all recorded pointers and see if
973e4b17023SJohn Marino they are still live. */
974e4b17023SJohn Marino void
ggc_prune_overhead_list(void)975e4b17023SJohn Marino ggc_prune_overhead_list (void)
976e4b17023SJohn Marino {
977e4b17023SJohn Marino htab_traverse (ptr_hash, ggc_prune_ptr, NULL);
978e4b17023SJohn Marino }
979e4b17023SJohn Marino
980e4b17023SJohn Marino /* Notice that the pointer has been freed. */
981e4b17023SJohn Marino void
ggc_free_overhead(void * ptr)982e4b17023SJohn Marino ggc_free_overhead (void *ptr)
983e4b17023SJohn Marino {
984e4b17023SJohn Marino PTR *slot = htab_find_slot_with_hash (ptr_hash, ptr, htab_hash_pointer (ptr),
985e4b17023SJohn Marino NO_INSERT);
986e4b17023SJohn Marino struct ptr_hash_entry *p;
987e4b17023SJohn Marino /* The pointer might be not found if a PCH read happened between allocation
988e4b17023SJohn Marino and ggc_free () call. FIXME: account memory properly in the presence of
989e4b17023SJohn Marino PCH. */
990e4b17023SJohn Marino if (!slot)
991e4b17023SJohn Marino return;
992e4b17023SJohn Marino p = (struct ptr_hash_entry *) *slot;
993e4b17023SJohn Marino p->loc->freed += p->size;
994e4b17023SJohn Marino htab_clear_slot (ptr_hash, slot);
995e4b17023SJohn Marino free (p);
996e4b17023SJohn Marino }
997e4b17023SJohn Marino
998e4b17023SJohn Marino /* Helper for qsort; sort descriptors by amount of memory consumed. */
999e4b17023SJohn Marino static int
final_cmp_statistic(const void * loc1,const void * loc2)1000e4b17023SJohn Marino final_cmp_statistic (const void *loc1, const void *loc2)
1001e4b17023SJohn Marino {
1002e4b17023SJohn Marino const struct loc_descriptor *const l1 =
1003e4b17023SJohn Marino *(const struct loc_descriptor *const *) loc1;
1004e4b17023SJohn Marino const struct loc_descriptor *const l2 =
1005e4b17023SJohn Marino *(const struct loc_descriptor *const *) loc2;
1006e4b17023SJohn Marino long diff;
1007e4b17023SJohn Marino diff = ((long)(l1->allocated + l1->overhead - l1->freed) -
1008e4b17023SJohn Marino (l2->allocated + l2->overhead - l2->freed));
1009e4b17023SJohn Marino return diff > 0 ? 1 : diff < 0 ? -1 : 0;
1010e4b17023SJohn Marino }
1011e4b17023SJohn Marino
1012e4b17023SJohn Marino /* Helper for qsort; sort descriptors by amount of memory consumed. */
1013e4b17023SJohn Marino static int
cmp_statistic(const void * loc1,const void * loc2)1014e4b17023SJohn Marino cmp_statistic (const void *loc1, const void *loc2)
1015e4b17023SJohn Marino {
1016e4b17023SJohn Marino const struct loc_descriptor *const l1 =
1017e4b17023SJohn Marino *(const struct loc_descriptor *const *) loc1;
1018e4b17023SJohn Marino const struct loc_descriptor *const l2 =
1019e4b17023SJohn Marino *(const struct loc_descriptor *const *) loc2;
1020e4b17023SJohn Marino long diff;
1021e4b17023SJohn Marino
1022e4b17023SJohn Marino diff = ((long)(l1->allocated + l1->overhead - l1->freed - l1->collected) -
1023e4b17023SJohn Marino (l2->allocated + l2->overhead - l2->freed - l2->collected));
1024e4b17023SJohn Marino if (diff)
1025e4b17023SJohn Marino return diff > 0 ? 1 : diff < 0 ? -1 : 0;
1026e4b17023SJohn Marino diff = ((long)(l1->allocated + l1->overhead - l1->freed) -
1027e4b17023SJohn Marino (l2->allocated + l2->overhead - l2->freed));
1028e4b17023SJohn Marino return diff > 0 ? 1 : diff < 0 ? -1 : 0;
1029e4b17023SJohn Marino }
1030e4b17023SJohn Marino
1031e4b17023SJohn Marino /* Collect array of the descriptors from hashtable. */
1032e4b17023SJohn Marino static struct loc_descriptor **loc_array;
1033e4b17023SJohn Marino static int
add_statistics(void ** slot,void * b)1034e4b17023SJohn Marino add_statistics (void **slot, void *b)
1035e4b17023SJohn Marino {
1036e4b17023SJohn Marino int *n = (int *)b;
1037e4b17023SJohn Marino loc_array[*n] = (struct loc_descriptor *) *slot;
1038e4b17023SJohn Marino (*n)++;
1039e4b17023SJohn Marino return 1;
1040e4b17023SJohn Marino }
1041e4b17023SJohn Marino
1042e4b17023SJohn Marino /* Dump per-site memory statistics. */
1043e4b17023SJohn Marino #endif
1044e4b17023SJohn Marino void
dump_ggc_loc_statistics(bool final ATTRIBUTE_UNUSED)1045e4b17023SJohn Marino dump_ggc_loc_statistics (bool final ATTRIBUTE_UNUSED)
1046e4b17023SJohn Marino {
1047e4b17023SJohn Marino #ifdef GATHER_STATISTICS
1048e4b17023SJohn Marino int nentries = 0;
1049e4b17023SJohn Marino char s[4096];
1050e4b17023SJohn Marino size_t collected = 0, freed = 0, allocated = 0, overhead = 0, times = 0;
1051e4b17023SJohn Marino int i;
1052e4b17023SJohn Marino
1053e4b17023SJohn Marino ggc_force_collect = true;
1054e4b17023SJohn Marino ggc_collect ();
1055e4b17023SJohn Marino
1056e4b17023SJohn Marino loc_array = XCNEWVEC (struct loc_descriptor *, loc_hash->n_elements);
1057e4b17023SJohn Marino fprintf (stderr, "-------------------------------------------------------\n");
1058e4b17023SJohn Marino fprintf (stderr, "\n%-48s %10s %10s %10s %10s %10s\n",
1059e4b17023SJohn Marino "source location", "Garbage", "Freed", "Leak", "Overhead", "Times");
1060e4b17023SJohn Marino fprintf (stderr, "-------------------------------------------------------\n");
1061e4b17023SJohn Marino htab_traverse (loc_hash, add_statistics, &nentries);
1062e4b17023SJohn Marino qsort (loc_array, nentries, sizeof (*loc_array),
1063e4b17023SJohn Marino final ? final_cmp_statistic : cmp_statistic);
1064e4b17023SJohn Marino for (i = 0; i < nentries; i++)
1065e4b17023SJohn Marino {
1066e4b17023SJohn Marino struct loc_descriptor *d = loc_array[i];
1067e4b17023SJohn Marino allocated += d->allocated;
1068e4b17023SJohn Marino times += d->times;
1069e4b17023SJohn Marino freed += d->freed;
1070e4b17023SJohn Marino collected += d->collected;
1071e4b17023SJohn Marino overhead += d->overhead;
1072e4b17023SJohn Marino }
1073e4b17023SJohn Marino for (i = 0; i < nentries; i++)
1074e4b17023SJohn Marino {
1075e4b17023SJohn Marino struct loc_descriptor *d = loc_array[i];
1076e4b17023SJohn Marino if (d->allocated)
1077e4b17023SJohn Marino {
1078e4b17023SJohn Marino const char *s1 = d->file;
1079e4b17023SJohn Marino const char *s2;
1080e4b17023SJohn Marino while ((s2 = strstr (s1, "gcc/")))
1081e4b17023SJohn Marino s1 = s2 + 4;
1082e4b17023SJohn Marino sprintf (s, "%s:%i (%s)", s1, d->line, d->function);
1083e4b17023SJohn Marino s[48] = 0;
1084e4b17023SJohn Marino fprintf (stderr, "%-48s %10li:%4.1f%% %10li:%4.1f%% %10li:%4.1f%% %10li:%4.1f%% %10li\n", s,
1085e4b17023SJohn Marino (long)d->collected,
1086e4b17023SJohn Marino (d->collected) * 100.0 / collected,
1087e4b17023SJohn Marino (long)d->freed,
1088e4b17023SJohn Marino (d->freed) * 100.0 / freed,
1089e4b17023SJohn Marino (long)(d->allocated + d->overhead - d->freed - d->collected),
1090e4b17023SJohn Marino (d->allocated + d->overhead - d->freed - d->collected) * 100.0
1091e4b17023SJohn Marino / (allocated + overhead - freed - collected),
1092e4b17023SJohn Marino (long)d->overhead,
1093e4b17023SJohn Marino d->overhead * 100.0 / overhead,
1094e4b17023SJohn Marino (long)d->times);
1095e4b17023SJohn Marino }
1096e4b17023SJohn Marino }
1097e4b17023SJohn Marino fprintf (stderr, "%-48s %10ld %10ld %10ld %10ld %10ld\n",
1098e4b17023SJohn Marino "Total", (long)collected, (long)freed,
1099e4b17023SJohn Marino (long)(allocated + overhead - freed - collected), (long)overhead,
1100e4b17023SJohn Marino (long)times);
1101e4b17023SJohn Marino fprintf (stderr, "%-48s %10s %10s %10s %10s %10s\n",
1102e4b17023SJohn Marino "source location", "Garbage", "Freed", "Leak", "Overhead", "Times");
1103e4b17023SJohn Marino fprintf (stderr, "-------------------------------------------------------\n");
1104e4b17023SJohn Marino ggc_force_collect = false;
1105e4b17023SJohn Marino #endif
1106e4b17023SJohn Marino }
1107