1 | /* |
---|
2 | * Copyright 1988, 1989 Hans-J. Boehm, Alan J. Demers |
---|
3 | * Copyright (c) 1991-1994 by Xerox Corporation. All rights reserved. |
---|
4 | * Copyright (c) 1999-2001 by Hewlett-Packard Company. All rights reserved. |
---|
5 | * |
---|
6 | * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED |
---|
7 | * OR IMPLIED. ANY USE IS AT YOUR OWN RISK. |
---|
8 | * |
---|
9 | * Permission is hereby granted to use or copy this program |
---|
10 | * for any purpose, provided the above notices are retained on all copies. |
---|
11 | * Permission to modify the code and to distribute modified code is granted, |
---|
12 | * provided the above notices are retained, and a notice that the code was |
---|
13 | * modified is included with the above copyright notice. |
---|
14 | */ |
---|
15 | /* Boehm, July 31, 1995 5:02 pm PDT */ |
---|
16 | |
---|
17 | |
---|
18 | #include <stdio.h> |
---|
19 | #include <limits.h> |
---|
20 | #ifndef _WIN32_WCE |
---|
21 | #include <signal.h> |
---|
22 | #endif |
---|
23 | |
---|
24 | #define I_HIDE_POINTERS /* To make GC_call_with_alloc_lock visible */ |
---|
25 | #include "private/gc_pmark.h" |
---|
26 | |
---|
27 | #ifdef GC_SOLARIS_THREADS |
---|
28 | # include <sys/syscall.h> |
---|
29 | #endif |
---|
30 | #if defined(MSWIN32) || defined(MSWINCE) |
---|
31 | # define WIN32_LEAN_AND_MEAN |
---|
32 | # define NOSERVICE |
---|
33 | # include <windows.h> |
---|
34 | # include <tchar.h> |
---|
35 | #endif |
---|
36 | |
---|
37 | # ifdef THREADS |
---|
38 | # ifdef PCR |
---|
39 | # include "il/PCR_IL.h" |
---|
40 | PCR_Th_ML GC_allocate_ml; |
---|
41 | # else |
---|
42 | # ifdef SRC_M3 |
---|
43 | /* Critical section counter is defined in the M3 runtime */ |
---|
44 | /* That's all we use. */ |
---|
45 | # else |
---|
46 | # ifdef GC_SOLARIS_THREADS |
---|
47 | mutex_t GC_allocate_ml; /* Implicitly initialized. */ |
---|
48 | # else |
---|
49 | # ifdef GC_WIN32_THREADS |
---|
50 | # if !defined(GC_NOT_DLL) && (defined(_DLL) || defined(GC_DLL)) |
---|
51 | __declspec(dllexport) CRITICAL_SECTION GC_allocate_ml; |
---|
52 | # else |
---|
53 | CRITICAL_SECTION GC_allocate_ml; |
---|
54 | # endif |
---|
55 | # else |
---|
56 | # if defined(GC_PTHREADS) && !defined(GC_SOLARIS_THREADS) |
---|
57 | # if defined(USE_SPIN_LOCK) |
---|
58 | pthread_t GC_lock_holder = NO_THREAD; |
---|
59 | # else |
---|
60 | pthread_mutex_t GC_allocate_ml = PTHREAD_MUTEX_INITIALIZER; |
---|
61 | pthread_t GC_lock_holder = NO_THREAD; |
---|
62 | /* Used only for assertions, and to prevent */ |
---|
63 | /* recursive reentry in the system call wrapper. */ |
---|
64 | # endif |
---|
65 | # else |
---|
66 | --> declare allocator lock here |
---|
67 | # endif |
---|
68 | # endif |
---|
69 | # endif |
---|
70 | # endif |
---|
71 | # endif |
---|
72 | # endif |
---|
73 | |
---|
74 | #if defined(NOSYS) || defined(ECOS) |
---|
75 | #undef STACKBASE |
---|
76 | #endif |
---|
77 | |
---|
78 | GC_FAR struct _GC_arrays GC_arrays /* = { 0 } */; |
---|
79 | |
---|
80 | |
---|
81 | GC_bool GC_debugging_started = FALSE; |
---|
82 | /* defined here so we don't have to load debug_malloc.o */ |
---|
83 | |
---|
84 | void (*GC_check_heap) GC_PROTO((void)) = (void (*) GC_PROTO((void)))0; |
---|
85 | |
---|
86 | void (*GC_start_call_back) GC_PROTO((void)) = (void (*) GC_PROTO((void)))0; |
---|
87 | |
---|
88 | ptr_t GC_stackbottom = 0; |
---|
89 | |
---|
90 | #ifdef IA64 |
---|
91 | ptr_t GC_register_stackbottom = 0; |
---|
92 | #endif |
---|
93 | |
---|
94 | GC_bool GC_dont_gc = 0; |
---|
95 | |
---|
96 | GC_bool GC_dont_precollect = 0; |
---|
97 | |
---|
98 | GC_bool GC_quiet = 0; |
---|
99 | |
---|
100 | GC_bool GC_print_stats = 0; |
---|
101 | |
---|
102 | GC_bool GC_print_back_height = 0; |
---|
103 | |
---|
104 | #ifdef FIND_LEAK |
---|
105 | int GC_find_leak = 1; |
---|
106 | #else |
---|
107 | int GC_find_leak = 0; |
---|
108 | #endif |
---|
109 | |
---|
110 | #ifdef ALL_INTERIOR_POINTERS |
---|
111 | int GC_all_interior_pointers = 1; |
---|
112 | #else |
---|
113 | int GC_all_interior_pointers = 0; |
---|
114 | #endif |
---|
115 | |
---|
116 | long GC_large_alloc_warn_interval = 5; |
---|
117 | /* Interval between unsuppressed warnings. */ |
---|
118 | |
---|
119 | long GC_large_alloc_warn_suppressed = 0; |
---|
120 | /* Number of warnings suppressed so far. */ |
---|
121 | |
---|
122 | /*ARGSUSED*/ |
---|
123 | GC_PTR GC_default_oom_fn GC_PROTO((size_t bytes_requested)) |
---|
124 | { |
---|
125 | return(0); |
---|
126 | } |
---|
127 | |
---|
128 | GC_PTR (*GC_oom_fn) GC_PROTO((size_t bytes_requested)) = GC_default_oom_fn; |
---|
129 | |
---|
130 | extern signed_word GC_mem_found; |
---|
131 | |
---|
132 | # ifdef MERGE_SIZES |
---|
133 | /* Set things up so that GC_size_map[i] >= words(i), */ |
---|
134 | /* but not too much bigger */ |
---|
135 | /* and so that size_map contains relatively few distinct entries */ |
---|
136 | /* This is stolen from Russ Atkinson's Cedar quantization */ |
---|
137 | /* alogrithm (but we precompute it). */ |
---|
138 | |
---|
139 | |
---|
140 | void GC_init_size_map() |
---|
141 | { |
---|
142 | register unsigned i; |
---|
143 | |
---|
144 | /* Map size 0 to something bigger. */ |
---|
145 | /* This avoids problems at lower levels. */ |
---|
146 | /* One word objects don't have to be 2 word aligned, */ |
---|
147 | /* unless we're using mark bytes. */ |
---|
148 | for (i = 0; i < sizeof(word); i++) { |
---|
149 | GC_size_map[i] = MIN_WORDS; |
---|
150 | } |
---|
151 | # if MIN_WORDS > 1 |
---|
152 | GC_size_map[sizeof(word)] = MIN_WORDS; |
---|
153 | # else |
---|
154 | GC_size_map[sizeof(word)] = ROUNDED_UP_WORDS(sizeof(word)); |
---|
155 | # endif |
---|
156 | for (i = sizeof(word) + 1; i <= 8 * sizeof(word); i++) { |
---|
157 | GC_size_map[i] = ALIGNED_WORDS(i); |
---|
158 | } |
---|
159 | for (i = 8*sizeof(word) + 1; i <= 16 * sizeof(word); i++) { |
---|
160 | GC_size_map[i] = (ROUNDED_UP_WORDS(i) + 1) & (~1); |
---|
161 | } |
---|
162 | # ifdef GC_GCJ_SUPPORT |
---|
163 | /* Make all sizes up to 32 words predictable, so that a */ |
---|
164 | /* compiler can statically perform the same computation, */ |
---|
165 | /* or at least a computation that results in similar size */ |
---|
166 | /* classes. */ |
---|
167 | for (i = 16*sizeof(word) + 1; i <= 32 * sizeof(word); i++) { |
---|
168 | GC_size_map[i] = (ROUNDED_UP_WORDS(i) + 3) & (~3); |
---|
169 | } |
---|
170 | # endif |
---|
171 | /* We leave the rest of the array to be filled in on demand. */ |
---|
172 | } |
---|
173 | |
---|
174 | /* Fill in additional entries in GC_size_map, including the ith one */ |
---|
175 | /* We assume the ith entry is currently 0. */ |
---|
176 | /* Note that a filled in section of the array ending at n always */ |
---|
177 | /* has length at least n/4. */ |
---|
178 | void GC_extend_size_map(i) |
---|
179 | word i; |
---|
180 | { |
---|
181 | word orig_word_sz = ROUNDED_UP_WORDS(i); |
---|
182 | word word_sz = orig_word_sz; |
---|
183 | register word byte_sz = WORDS_TO_BYTES(word_sz); |
---|
184 | /* The size we try to preserve. */ |
---|
185 | /* Close to to i, unless this would */ |
---|
186 | /* introduce too many distinct sizes. */ |
---|
187 | word smaller_than_i = byte_sz - (byte_sz >> 3); |
---|
188 | word much_smaller_than_i = byte_sz - (byte_sz >> 2); |
---|
189 | register word low_limit; /* The lowest indexed entry we */ |
---|
190 | /* initialize. */ |
---|
191 | register word j; |
---|
192 | |
---|
193 | if (GC_size_map[smaller_than_i] == 0) { |
---|
194 | low_limit = much_smaller_than_i; |
---|
195 | while (GC_size_map[low_limit] != 0) low_limit++; |
---|
196 | } else { |
---|
197 | low_limit = smaller_than_i + 1; |
---|
198 | while (GC_size_map[low_limit] != 0) low_limit++; |
---|
199 | word_sz = ROUNDED_UP_WORDS(low_limit); |
---|
200 | word_sz += word_sz >> 3; |
---|
201 | if (word_sz < orig_word_sz) word_sz = orig_word_sz; |
---|
202 | } |
---|
203 | # ifdef ALIGN_DOUBLE |
---|
204 | word_sz += 1; |
---|
205 | word_sz &= ~1; |
---|
206 | # endif |
---|
207 | if (word_sz > MAXOBJSZ) { |
---|
208 | word_sz = MAXOBJSZ; |
---|
209 | } |
---|
210 | /* If we can fit the same number of larger objects in a block, */ |
---|
211 | /* do so. */ |
---|
212 | { |
---|
213 | size_t number_of_objs = BODY_SZ/word_sz; |
---|
214 | word_sz = BODY_SZ/number_of_objs; |
---|
215 | # ifdef ALIGN_DOUBLE |
---|
216 | word_sz &= ~1; |
---|
217 | # endif |
---|
218 | } |
---|
219 | byte_sz = WORDS_TO_BYTES(word_sz); |
---|
220 | if (GC_all_interior_pointers) { |
---|
221 | /* We need one extra byte; don't fill in GC_size_map[byte_sz] */ |
---|
222 | byte_sz--; |
---|
223 | } |
---|
224 | |
---|
225 | for (j = low_limit; j <= byte_sz; j++) GC_size_map[j] = word_sz; |
---|
226 | } |
---|
227 | # endif |
---|
228 | |
---|
229 | |
---|
230 | /* |
---|
231 | * The following is a gross hack to deal with a problem that can occur |
---|
232 | * on machines that are sloppy about stack frame sizes, notably SPARC. |
---|
233 | * Bogus pointers may be written to the stack and not cleared for |
---|
234 | * a LONG time, because they always fall into holes in stack frames |
---|
235 | * that are not written. We partially address this by clearing |
---|
236 | * sections of the stack whenever we get control. |
---|
237 | */ |
---|
238 | word GC_stack_last_cleared = 0; /* GC_no when we last did this */ |
---|
239 | # ifdef THREADS |
---|
240 | # define BIG_CLEAR_SIZE 2048 /* Clear this much now and then. */ |
---|
241 | # define SMALL_CLEAR_SIZE 256 /* Clear this much every time. */ |
---|
242 | # endif |
---|
243 | # define CLEAR_SIZE 213 /* Granularity for GC_clear_stack_inner */ |
---|
244 | # define DEGRADE_RATE 50 |
---|
245 | |
---|
246 | word GC_min_sp; /* Coolest stack pointer value from which we've */ |
---|
247 | /* already cleared the stack. */ |
---|
248 | |
---|
249 | word GC_high_water; |
---|
250 | /* "hottest" stack pointer value we have seen */ |
---|
251 | /* recently. Degrades over time. */ |
---|
252 | |
---|
253 | word GC_words_allocd_at_reset; |
---|
254 | |
---|
255 | #if defined(ASM_CLEAR_CODE) |
---|
256 | extern ptr_t GC_clear_stack_inner(); |
---|
257 | #else |
---|
258 | /* Clear the stack up to about limit. Return arg. */ |
---|
259 | /*ARGSUSED*/ |
---|
260 | ptr_t GC_clear_stack_inner(arg, limit) |
---|
261 | ptr_t arg; |
---|
262 | word limit; |
---|
263 | { |
---|
264 | word dummy[CLEAR_SIZE]; |
---|
265 | |
---|
266 | BZERO(dummy, CLEAR_SIZE*sizeof(word)); |
---|
267 | if ((word)(dummy) COOLER_THAN limit) { |
---|
268 | (void) GC_clear_stack_inner(arg, limit); |
---|
269 | } |
---|
270 | /* Make sure the recursive call is not a tail call, and the bzero */ |
---|
271 | /* call is not recognized as dead code. */ |
---|
272 | GC_noop1((word)dummy); |
---|
273 | return(arg); |
---|
274 | } |
---|
275 | #endif |
---|
276 | |
---|
277 | /* Clear some of the inaccessible part of the stack. Returns its */ |
---|
278 | /* argument, so it can be used in a tail call position, hence clearing */ |
---|
279 | /* another frame. */ |
---|
280 | ptr_t GC_clear_stack(arg) |
---|
281 | ptr_t arg; |
---|
282 | { |
---|
283 | register word sp = (word)GC_approx_sp(); /* Hotter than actual sp */ |
---|
284 | # ifdef THREADS |
---|
285 | word dummy[SMALL_CLEAR_SIZE]; |
---|
286 | static unsigned random_no = 0; |
---|
287 | /* Should be more random than it is ... */ |
---|
288 | /* Used to occasionally clear a bigger */ |
---|
289 | /* chunk. */ |
---|
290 | # endif |
---|
291 | register word limit; |
---|
292 | |
---|
293 | # define SLOP 400 |
---|
294 | /* Extra bytes we clear every time. This clears our own */ |
---|
295 | /* activation record, and should cause more frequent */ |
---|
296 | /* clearing near the cold end of the stack, a good thing. */ |
---|
297 | # define GC_SLOP 4000 |
---|
298 | /* We make GC_high_water this much hotter than we really saw */ |
---|
299 | /* saw it, to cover for GC noise etc. above our current frame. */ |
---|
300 | # define CLEAR_THRESHOLD 100000 |
---|
301 | /* We restart the clearing process after this many bytes of */ |
---|
302 | /* allocation. Otherwise very heavily recursive programs */ |
---|
303 | /* with sparse stacks may result in heaps that grow almost */ |
---|
304 | /* without bounds. As the heap gets larger, collection */ |
---|
305 | /* frequency decreases, thus clearing frequency would decrease, */ |
---|
306 | /* thus more junk remains accessible, thus the heap gets */ |
---|
307 | /* larger ... */ |
---|
308 | # ifdef THREADS |
---|
309 | if (++random_no % 13 == 0) { |
---|
310 | limit = sp; |
---|
311 | MAKE_HOTTER(limit, BIG_CLEAR_SIZE*sizeof(word)); |
---|
312 | limit &= ~0xf; /* Make it sufficiently aligned for assembly */ |
---|
313 | /* implementations of GC_clear_stack_inner. */ |
---|
314 | return GC_clear_stack_inner(arg, limit); |
---|
315 | } else { |
---|
316 | BZERO(dummy, SMALL_CLEAR_SIZE*sizeof(word)); |
---|
317 | return arg; |
---|
318 | } |
---|
319 | # else |
---|
320 | if (GC_gc_no > GC_stack_last_cleared) { |
---|
321 | /* Start things over, so we clear the entire stack again */ |
---|
322 | if (GC_stack_last_cleared == 0) GC_high_water = (word) GC_stackbottom; |
---|
323 | GC_min_sp = GC_high_water; |
---|
324 | GC_stack_last_cleared = GC_gc_no; |
---|
325 | GC_words_allocd_at_reset = GC_words_allocd; |
---|
326 | } |
---|
327 | /* Adjust GC_high_water */ |
---|
328 | MAKE_COOLER(GC_high_water, WORDS_TO_BYTES(DEGRADE_RATE) + GC_SLOP); |
---|
329 | if (sp HOTTER_THAN GC_high_water) { |
---|
330 | GC_high_water = sp; |
---|
331 | } |
---|
332 | MAKE_HOTTER(GC_high_water, GC_SLOP); |
---|
333 | limit = GC_min_sp; |
---|
334 | MAKE_HOTTER(limit, SLOP); |
---|
335 | if (sp COOLER_THAN limit) { |
---|
336 | limit &= ~0xf; /* Make it sufficiently aligned for assembly */ |
---|
337 | /* implementations of GC_clear_stack_inner. */ |
---|
338 | GC_min_sp = sp; |
---|
339 | return(GC_clear_stack_inner(arg, limit)); |
---|
340 | } else if (WORDS_TO_BYTES(GC_words_allocd - GC_words_allocd_at_reset) |
---|
341 | > CLEAR_THRESHOLD) { |
---|
342 | /* Restart clearing process, but limit how much clearing we do. */ |
---|
343 | GC_min_sp = sp; |
---|
344 | MAKE_HOTTER(GC_min_sp, CLEAR_THRESHOLD/4); |
---|
345 | if (GC_min_sp HOTTER_THAN GC_high_water) GC_min_sp = GC_high_water; |
---|
346 | GC_words_allocd_at_reset = GC_words_allocd; |
---|
347 | } |
---|
348 | return(arg); |
---|
349 | # endif |
---|
350 | } |
---|
351 | |
---|
352 | |
---|
353 | /* Return a pointer to the base address of p, given a pointer to a */ |
---|
354 | /* an address within an object. Return 0 o.w. */ |
---|
355 | # ifdef __STDC__ |
---|
356 | GC_PTR GC_base(GC_PTR p) |
---|
357 | # else |
---|
358 | GC_PTR GC_base(p) |
---|
359 | GC_PTR p; |
---|
360 | # endif |
---|
361 | { |
---|
362 | register word r; |
---|
363 | register struct hblk *h; |
---|
364 | register bottom_index *bi; |
---|
365 | register hdr *candidate_hdr; |
---|
366 | register word limit; |
---|
367 | |
---|
368 | r = (word)p; |
---|
369 | if (!GC_is_initialized) return 0; |
---|
370 | h = HBLKPTR(r); |
---|
371 | GET_BI(r, bi); |
---|
372 | candidate_hdr = HDR_FROM_BI(bi, r); |
---|
373 | if (candidate_hdr == 0) return(0); |
---|
374 | /* If it's a pointer to the middle of a large object, move it */ |
---|
375 | /* to the beginning. */ |
---|
376 | while (IS_FORWARDING_ADDR_OR_NIL(candidate_hdr)) { |
---|
377 | h = FORWARDED_ADDR(h,candidate_hdr); |
---|
378 | r = (word)h; |
---|
379 | candidate_hdr = HDR(h); |
---|
380 | } |
---|
381 | if (candidate_hdr -> hb_map == GC_invalid_map) return(0); |
---|
382 | /* Make sure r points to the beginning of the object */ |
---|
383 | r &= ~(WORDS_TO_BYTES(1) - 1); |
---|
384 | { |
---|
385 | register int offset = HBLKDISPL(r); |
---|
386 | register signed_word sz = candidate_hdr -> hb_sz; |
---|
387 | register signed_word map_entry; |
---|
388 | |
---|
389 | map_entry = MAP_ENTRY((candidate_hdr -> hb_map), offset); |
---|
390 | if (map_entry > CPP_MAX_OFFSET) { |
---|
391 | map_entry = (signed_word)(BYTES_TO_WORDS(offset)) % sz; |
---|
392 | } |
---|
393 | r -= WORDS_TO_BYTES(map_entry); |
---|
394 | limit = r + WORDS_TO_BYTES(sz); |
---|
395 | if (limit > (word)(h + 1) |
---|
396 | && sz <= BYTES_TO_WORDS(HBLKSIZE)) { |
---|
397 | return(0); |
---|
398 | } |
---|
399 | if ((word)p >= limit) return(0); |
---|
400 | } |
---|
401 | return((GC_PTR)r); |
---|
402 | } |
---|
403 | |
---|
404 | |
---|
405 | /* Return the size of an object, given a pointer to its base. */ |
---|
406 | /* (For small obects this also happens to work from interior pointers, */ |
---|
407 | /* but that shouldn't be relied upon.) */ |
---|
408 | # ifdef __STDC__ |
---|
409 | size_t GC_size(GC_PTR p) |
---|
410 | # else |
---|
411 | size_t GC_size(p) |
---|
412 | GC_PTR p; |
---|
413 | # endif |
---|
414 | { |
---|
415 | register int sz; |
---|
416 | register hdr * hhdr = HDR(p); |
---|
417 | |
---|
418 | sz = WORDS_TO_BYTES(hhdr -> hb_sz); |
---|
419 | return(sz); |
---|
420 | } |
---|
421 | |
---|
422 | size_t GC_get_heap_size GC_PROTO(()) |
---|
423 | { |
---|
424 | return ((size_t) GC_heapsize); |
---|
425 | } |
---|
426 | |
---|
427 | size_t GC_get_free_bytes GC_PROTO(()) |
---|
428 | { |
---|
429 | return ((size_t) GC_large_free_bytes); |
---|
430 | } |
---|
431 | |
---|
432 | size_t GC_get_bytes_since_gc GC_PROTO(()) |
---|
433 | { |
---|
434 | return ((size_t) WORDS_TO_BYTES(GC_words_allocd)); |
---|
435 | } |
---|
436 | |
---|
437 | size_t GC_get_total_bytes GC_PROTO(()) |
---|
438 | { |
---|
439 | return ((size_t) WORDS_TO_BYTES(GC_words_allocd+GC_words_allocd_before_gc)); |
---|
440 | } |
---|
441 | |
---|
442 | GC_bool GC_is_initialized = FALSE; |
---|
443 | |
---|
444 | void GC_init() |
---|
445 | { |
---|
446 | DCL_LOCK_STATE; |
---|
447 | |
---|
448 | DISABLE_SIGNALS(); |
---|
449 | |
---|
450 | #ifdef MSWIN32 |
---|
451 | if (!GC_is_initialized) InitializeCriticalSection(&GC_allocate_ml); |
---|
452 | #endif /* MSWIN32 */ |
---|
453 | |
---|
454 | LOCK(); |
---|
455 | GC_init_inner(); |
---|
456 | UNLOCK(); |
---|
457 | ENABLE_SIGNALS(); |
---|
458 | |
---|
459 | # if defined(PARALLEL_MARK) || defined(THREAD_LOCAL_ALLOC) |
---|
460 | /* Make sure marker threads and started and thread local */ |
---|
461 | /* allocation is initialized, in case we didn't get */ |
---|
462 | /* called from GC_init_parallel(); */ |
---|
463 | { |
---|
464 | extern void GC_init_parallel(void); |
---|
465 | GC_init_parallel(); |
---|
466 | } |
---|
467 | # endif /* PARALLEL_MARK || THREAD_LOCAL_ALLOC */ |
---|
468 | } |
---|
469 | |
---|
470 | #if defined(MSWIN32) || defined(MSWINCE) |
---|
471 | CRITICAL_SECTION GC_write_cs; |
---|
472 | #endif |
---|
473 | |
---|
474 | #ifdef MSWIN32 |
---|
475 | extern void GC_init_win32 GC_PROTO((void)); |
---|
476 | #endif |
---|
477 | |
---|
478 | extern void GC_setpagesize(); |
---|
479 | |
---|
480 | #ifdef UNIX_LIKE |
---|
481 | |
---|
482 | extern void GC_set_and_save_fault_handler GC_PROTO((void (*handler)(int))); |
---|
483 | |
---|
484 | static void looping_handler(sig) |
---|
485 | int sig; |
---|
486 | { |
---|
487 | GC_err_printf1("Caught signal %d: looping in handler\n", sig); |
---|
488 | for(;;); |
---|
489 | } |
---|
490 | #endif |
---|
491 | |
---|
492 | #ifdef MSWIN32 |
---|
493 | extern GC_bool GC_no_win32_dlls; |
---|
494 | #else |
---|
495 | # define GC_no_win32_dlls FALSE |
---|
496 | #endif |
---|
497 | |
---|
498 | void GC_init_inner() |
---|
499 | { |
---|
500 | # if !defined(THREADS) && defined(GC_ASSERTIONS) |
---|
501 | word dummy; |
---|
502 | # endif |
---|
503 | word initial_heap_sz = (word)MINHINCR; |
---|
504 | |
---|
505 | if (GC_is_initialized) return; |
---|
506 | # ifdef PRINTSTATS |
---|
507 | GC_print_stats = 1; |
---|
508 | # endif |
---|
509 | # if defined(MSWIN32) || defined(MSWINCE) |
---|
510 | InitializeCriticalSection(&GC_write_cs); |
---|
511 | # endif |
---|
512 | |
---|
513 | if (0 != GETENV("GC_PRINT_STATS")) { |
---|
514 | GC_print_stats = 1; |
---|
515 | } |
---|
516 | if (0 != GETENV("GC_FIND_LEAK")) { |
---|
517 | GC_find_leak = 1; |
---|
518 | } |
---|
519 | if (0 != GETENV("GC_ALL_INTERIOR_POINTERS")) { |
---|
520 | GC_all_interior_pointers = 1; |
---|
521 | } |
---|
522 | if (0 != GETENV("GC_DONT_GC")) { |
---|
523 | GC_dont_gc = 1; |
---|
524 | } |
---|
525 | if (0 != GETENV("GC_PRINT_BACK_HEIGHT")) { |
---|
526 | GC_print_back_height = 1; |
---|
527 | } |
---|
528 | if (0 != GETENV("GC_NO_BLACKLIST_WARNING")) { |
---|
529 | GC_large_alloc_warn_interval = LONG_MAX; |
---|
530 | } |
---|
531 | { |
---|
532 | char * time_limit_string = GETENV("GC_PAUSE_TIME_TARGET"); |
---|
533 | if (0 != time_limit_string) { |
---|
534 | long time_limit = atol(time_limit_string); |
---|
535 | if (time_limit < 5) { |
---|
536 | WARN("GC_PAUSE_TIME_TARGET environment variable value too small " |
---|
537 | "or bad syntax: Ignoring\n", 0); |
---|
538 | } else { |
---|
539 | GC_time_limit = time_limit; |
---|
540 | } |
---|
541 | } |
---|
542 | } |
---|
543 | { |
---|
544 | char * interval_string = GETENV("GC_LARGE_ALLOC_WARN_INTERVAL"); |
---|
545 | if (0 != interval_string) { |
---|
546 | long interval = atol(interval_string); |
---|
547 | if (interval <= 0) { |
---|
548 | WARN("GC_LARGE_ALLOC_WARN_INTERVAL environment variable has " |
---|
549 | "bad value: Ignoring\n", 0); |
---|
550 | } else { |
---|
551 | GC_large_alloc_warn_interval = interval; |
---|
552 | } |
---|
553 | } |
---|
554 | } |
---|
555 | # ifdef UNIX_LIKE |
---|
556 | if (0 != GETENV("GC_LOOP_ON_ABORT")) { |
---|
557 | GC_set_and_save_fault_handler(looping_handler); |
---|
558 | } |
---|
559 | # endif |
---|
560 | /* Adjust normal object descriptor for extra allocation. */ |
---|
561 | if (ALIGNMENT > GC_DS_TAGS && EXTRA_BYTES != 0) { |
---|
562 | GC_obj_kinds[NORMAL].ok_descriptor = ((word)(-ALIGNMENT) | GC_DS_LENGTH); |
---|
563 | } |
---|
564 | GC_setpagesize(); |
---|
565 | GC_exclude_static_roots(beginGC_arrays, endGC_arrays); |
---|
566 | GC_exclude_static_roots(beginGC_obj_kinds, endGC_obj_kinds); |
---|
567 | # ifdef SEPARATE_GLOBALS |
---|
568 | GC_exclude_static_roots(beginGC_objfreelist, endGC_objfreelist); |
---|
569 | GC_exclude_static_roots(beginGC_aobjfreelist, endGC_aobjfreelist); |
---|
570 | # endif |
---|
571 | # ifdef MSWIN32 |
---|
572 | GC_init_win32(); |
---|
573 | # endif |
---|
574 | # if defined(SEARCH_FOR_DATA_START) |
---|
575 | GC_init_linux_data_start(); |
---|
576 | # endif |
---|
577 | # if (defined(NETBSD) || defined(OPENBSD)) && defined(__ELF__) |
---|
578 | GC_init_netbsd_elf(); |
---|
579 | # endif |
---|
580 | # if defined(GC_PTHREADS) || defined(GC_SOLARIS_THREADS) |
---|
581 | GC_thr_init(); |
---|
582 | # endif |
---|
583 | # ifdef GC_SOLARIS_THREADS |
---|
584 | /* We need dirty bits in order to find live stack sections. */ |
---|
585 | GC_dirty_init(); |
---|
586 | # endif |
---|
587 | # if !defined(THREADS) || defined(GC_PTHREADS) || defined(GC_WIN32_THREADS) \ |
---|
588 | || defined(GC_SOLARIS_THREADS) |
---|
589 | if (GC_stackbottom == 0) { |
---|
590 | GC_stackbottom = GC_get_stack_base(); |
---|
591 | # if defined(LINUX) && defined(IA64) |
---|
592 | GC_register_stackbottom = GC_get_register_stack_base(); |
---|
593 | # endif |
---|
594 | } |
---|
595 | # endif |
---|
596 | GC_ASSERT(sizeof (ptr_t) == sizeof(word)); |
---|
597 | GC_ASSERT(sizeof (signed_word) == sizeof(word)); |
---|
598 | GC_ASSERT(sizeof (struct hblk) == HBLKSIZE); |
---|
599 | # ifndef THREADS |
---|
600 | # if defined(STACK_GROWS_UP) && defined(STACK_GROWS_DOWN) |
---|
601 | ABORT( |
---|
602 | "Only one of STACK_GROWS_UP and STACK_GROWS_DOWN should be defd\n"); |
---|
603 | # endif |
---|
604 | # if !defined(STACK_GROWS_UP) && !defined(STACK_GROWS_DOWN) |
---|
605 | ABORT( |
---|
606 | "One of STACK_GROWS_UP and STACK_GROWS_DOWN should be defd\n"); |
---|
607 | # endif |
---|
608 | # ifdef STACK_GROWS_DOWN |
---|
609 | GC_ASSERT((word)(&dummy) <= (word)GC_stackbottom); |
---|
610 | # else |
---|
611 | GC_ASSERT((word)(&dummy) >= (word)GC_stackbottom); |
---|
612 | # endif |
---|
613 | # endif |
---|
614 | # if !defined(_AUX_SOURCE) || defined(__GNUC__) |
---|
615 | GC_ASSERT((word)(-1) > (word)0); |
---|
616 | /* word should be unsigned */ |
---|
617 | # endif |
---|
618 | GC_ASSERT((signed_word)(-1) < (signed_word)0); |
---|
619 | |
---|
620 | /* Add initial guess of root sets. Do this first, since sbrk(0) */ |
---|
621 | /* might be used. */ |
---|
622 | GC_register_data_segments(); |
---|
623 | GC_init_headers(); |
---|
624 | GC_bl_init(); |
---|
625 | GC_mark_init(); |
---|
626 | { |
---|
627 | char * sz_str = GETENV("GC_INITIAL_HEAP_SIZE"); |
---|
628 | if (sz_str != NULL) { |
---|
629 | initial_heap_sz = atoi(sz_str); |
---|
630 | if (initial_heap_sz <= MINHINCR * HBLKSIZE) { |
---|
631 | WARN("Bad initial heap size %s - ignoring it.\n", |
---|
632 | sz_str); |
---|
633 | } |
---|
634 | initial_heap_sz = divHBLKSZ(initial_heap_sz); |
---|
635 | } |
---|
636 | } |
---|
637 | if (!GC_expand_hp_inner(initial_heap_sz)) { |
---|
638 | GC_err_printf0("Can't start up: not enough memory\n"); |
---|
639 | EXIT(); |
---|
640 | } |
---|
641 | /* Preallocate large object map. It's otherwise inconvenient to */ |
---|
642 | /* deal with failure. */ |
---|
643 | if (!GC_add_map_entry((word)0)) { |
---|
644 | GC_err_printf0("Can't start up: not enough memory\n"); |
---|
645 | EXIT(); |
---|
646 | } |
---|
647 | GC_register_displacement_inner(0L); |
---|
648 | # ifdef MERGE_SIZES |
---|
649 | GC_init_size_map(); |
---|
650 | # endif |
---|
651 | # ifdef PCR |
---|
652 | if (PCR_IL_Lock(PCR_Bool_false, PCR_allSigsBlocked, PCR_waitForever) |
---|
653 | != PCR_ERes_okay) { |
---|
654 | ABORT("Can't lock load state\n"); |
---|
655 | } else if (PCR_IL_Unlock() != PCR_ERes_okay) { |
---|
656 | ABORT("Can't unlock load state\n"); |
---|
657 | } |
---|
658 | PCR_IL_Unlock(); |
---|
659 | GC_pcr_install(); |
---|
660 | # endif |
---|
661 | # if !defined(SMALL_CONFIG) |
---|
662 | if (!GC_no_win32_dlls && 0 != GETENV("GC_ENABLE_INCREMENTAL")) { |
---|
663 | GC_ASSERT(!GC_incremental); |
---|
664 | GC_setpagesize(); |
---|
665 | # ifndef GC_SOLARIS_THREADS |
---|
666 | GC_dirty_init(); |
---|
667 | # endif |
---|
668 | GC_ASSERT(GC_words_allocd == 0) |
---|
669 | GC_incremental = TRUE; |
---|
670 | } |
---|
671 | # endif /* !SMALL_CONFIG */ |
---|
672 | /* Get black list set up and/or incrmental GC started */ |
---|
673 | if (!GC_dont_precollect || GC_incremental) GC_gcollect_inner(); |
---|
674 | GC_is_initialized = TRUE; |
---|
675 | # ifdef STUBBORN_ALLOC |
---|
676 | GC_stubborn_init(); |
---|
677 | # endif |
---|
678 | /* Convince lint that some things are used */ |
---|
679 | # ifdef LINT |
---|
680 | { |
---|
681 | extern char * GC_copyright[]; |
---|
682 | extern int GC_read(); |
---|
683 | extern void GC_register_finalizer_no_order(); |
---|
684 | |
---|
685 | GC_noop(GC_copyright, GC_find_header, |
---|
686 | GC_push_one, GC_call_with_alloc_lock, GC_read, |
---|
687 | GC_dont_expand, |
---|
688 | # ifndef NO_DEBUGGING |
---|
689 | GC_dump, |
---|
690 | # endif |
---|
691 | GC_register_finalizer_no_order); |
---|
692 | } |
---|
693 | # endif |
---|
694 | } |
---|
695 | |
---|
696 | void GC_enable_incremental GC_PROTO(()) |
---|
697 | { |
---|
698 | # if !defined(SMALL_CONFIG) |
---|
699 | if (!GC_find_leak) { |
---|
700 | DCL_LOCK_STATE; |
---|
701 | |
---|
702 | DISABLE_SIGNALS(); |
---|
703 | LOCK(); |
---|
704 | if (GC_incremental) goto out; |
---|
705 | GC_setpagesize(); |
---|
706 | if (GC_no_win32_dlls) goto out; |
---|
707 | # ifndef GC_SOLARIS_THREADS |
---|
708 | GC_dirty_init(); |
---|
709 | # endif |
---|
710 | if (!GC_is_initialized) { |
---|
711 | GC_init_inner(); |
---|
712 | } |
---|
713 | if (GC_incremental) goto out; |
---|
714 | if (GC_dont_gc) { |
---|
715 | /* Can't easily do it. */ |
---|
716 | UNLOCK(); |
---|
717 | ENABLE_SIGNALS(); |
---|
718 | return; |
---|
719 | } |
---|
720 | if (GC_words_allocd > 0) { |
---|
721 | /* There may be unmarked reachable objects */ |
---|
722 | GC_gcollect_inner(); |
---|
723 | } /* else we're OK in assuming everything's */ |
---|
724 | /* clean since nothing can point to an */ |
---|
725 | /* unmarked object. */ |
---|
726 | GC_read_dirty(); |
---|
727 | GC_incremental = TRUE; |
---|
728 | out: |
---|
729 | UNLOCK(); |
---|
730 | ENABLE_SIGNALS(); |
---|
731 | } |
---|
732 | # endif |
---|
733 | } |
---|
734 | |
---|
735 | |
---|
736 | #if defined(MSWIN32) || defined(MSWINCE) |
---|
737 | # define LOG_FILE _T("gc.log") |
---|
738 | |
---|
739 | HANDLE GC_stdout = 0; |
---|
740 | |
---|
741 | void GC_deinit() |
---|
742 | { |
---|
743 | if (GC_is_initialized) { |
---|
744 | DeleteCriticalSection(&GC_write_cs); |
---|
745 | } |
---|
746 | } |
---|
747 | |
---|
748 | int GC_write(buf, len) |
---|
749 | GC_CONST char * buf; |
---|
750 | size_t len; |
---|
751 | { |
---|
752 | BOOL tmp; |
---|
753 | DWORD written; |
---|
754 | if (len == 0) |
---|
755 | return 0; |
---|
756 | EnterCriticalSection(&GC_write_cs); |
---|
757 | if (GC_stdout == INVALID_HANDLE_VALUE) { |
---|
758 | return -1; |
---|
759 | } else if (GC_stdout == 0) { |
---|
760 | GC_stdout = CreateFile(LOG_FILE, GENERIC_WRITE, |
---|
761 | FILE_SHARE_READ | FILE_SHARE_WRITE, |
---|
762 | NULL, CREATE_ALWAYS, FILE_FLAG_WRITE_THROUGH, |
---|
763 | NULL); |
---|
764 | if (GC_stdout == INVALID_HANDLE_VALUE) ABORT("Open of log file failed"); |
---|
765 | } |
---|
766 | tmp = WriteFile(GC_stdout, buf, len, &written, NULL); |
---|
767 | if (!tmp) |
---|
768 | DebugBreak(); |
---|
769 | LeaveCriticalSection(&GC_write_cs); |
---|
770 | return tmp ? (int)written : -1; |
---|
771 | } |
---|
772 | |
---|
773 | #endif |
---|
774 | |
---|
775 | #if defined(OS2) || defined(MACOS) |
---|
776 | FILE * GC_stdout = NULL; |
---|
777 | FILE * GC_stderr = NULL; |
---|
778 | int GC_tmp; /* Should really be local ... */ |
---|
779 | |
---|
780 | void GC_set_files() |
---|
781 | { |
---|
782 | if (GC_stdout == NULL) { |
---|
783 | GC_stdout = stdout; |
---|
784 | } |
---|
785 | if (GC_stderr == NULL) { |
---|
786 | GC_stderr = stderr; |
---|
787 | } |
---|
788 | } |
---|
789 | #endif |
---|
790 | |
---|
791 | #if !defined(OS2) && !defined(MACOS) && !defined(MSWIN32) && !defined(MSWINCE) |
---|
792 | int GC_stdout = 1; |
---|
793 | int GC_stderr = 2; |
---|
794 | # if !defined(AMIGA) |
---|
795 | # include <unistd.h> |
---|
796 | # endif |
---|
797 | #endif |
---|
798 | |
---|
799 | #if !defined(MSWIN32) && !defined(MSWINCE) && !defined(OS2) \ |
---|
800 | && !defined(MACOS) && !defined(ECOS) && !defined(NOSYS) |
---|
801 | int GC_write(fd, buf, len) |
---|
802 | int fd; |
---|
803 | GC_CONST char *buf; |
---|
804 | size_t len; |
---|
805 | { |
---|
806 | register int bytes_written = 0; |
---|
807 | register int result; |
---|
808 | |
---|
809 | while (bytes_written < len) { |
---|
810 | # ifdef GC_SOLARIS_THREADS |
---|
811 | result = syscall(SYS_write, fd, buf + bytes_written, |
---|
812 | len - bytes_written); |
---|
813 | # else |
---|
814 | result = write(fd, buf + bytes_written, len - bytes_written); |
---|
815 | # endif |
---|
816 | if (-1 == result) return(result); |
---|
817 | bytes_written += result; |
---|
818 | } |
---|
819 | return(bytes_written); |
---|
820 | } |
---|
821 | #endif /* UN*X */ |
---|
822 | |
---|
823 | #ifdef ECOS |
---|
824 | int GC_write(fd, buf, len) |
---|
825 | { |
---|
826 | _Jv_diag_write (buf, len); |
---|
827 | return len; |
---|
828 | } |
---|
829 | #endif |
---|
830 | |
---|
831 | #ifdef NOSYS |
---|
832 | int GC_write(fd, buf, len) |
---|
833 | { |
---|
834 | /* No writing. */ |
---|
835 | return len; |
---|
836 | } |
---|
837 | #endif |
---|
838 | |
---|
839 | |
---|
840 | #if defined(MSWIN32) || defined(MSWINCE) |
---|
841 | # define WRITE(f, buf, len) GC_write(buf, len) |
---|
842 | #else |
---|
843 | # if defined(OS2) || defined(MACOS) |
---|
844 | # define WRITE(f, buf, len) (GC_set_files(), \ |
---|
845 | GC_tmp = fwrite((buf), 1, (len), (f)), \ |
---|
846 | fflush(f), GC_tmp) |
---|
847 | # else |
---|
848 | # define WRITE(f, buf, len) GC_write((f), (buf), (len)) |
---|
849 | # endif |
---|
850 | #endif |
---|
851 | |
---|
852 | /* A version of printf that is unlikely to call malloc, and is thus safer */ |
---|
853 | /* to call from the collector in case malloc has been bound to GC_malloc. */ |
---|
854 | /* Assumes that no more than 1023 characters are written at once. */ |
---|
855 | /* Assumes that all arguments have been converted to something of the */ |
---|
856 | /* same size as long, and that the format conversions expect something */ |
---|
857 | /* of that size. */ |
---|
858 | void GC_printf(format, a, b, c, d, e, f) |
---|
859 | GC_CONST char * format; |
---|
860 | long a, b, c, d, e, f; |
---|
861 | { |
---|
862 | char buf[1025]; |
---|
863 | |
---|
864 | if (GC_quiet) return; |
---|
865 | buf[1024] = 0x15; |
---|
866 | (void) sprintf(buf, format, a, b, c, d, e, f); |
---|
867 | if (buf[1024] != 0x15) ABORT("GC_printf clobbered stack"); |
---|
868 | if (WRITE(GC_stdout, buf, strlen(buf)) < 0) ABORT("write to stdout failed"); |
---|
869 | } |
---|
870 | |
---|
871 | void GC_err_printf(format, a, b, c, d, e, f) |
---|
872 | GC_CONST char * format; |
---|
873 | long a, b, c, d, e, f; |
---|
874 | { |
---|
875 | char buf[1025]; |
---|
876 | |
---|
877 | buf[1024] = 0x15; |
---|
878 | (void) sprintf(buf, format, a, b, c, d, e, f); |
---|
879 | if (buf[1024] != 0x15) ABORT("GC_err_printf clobbered stack"); |
---|
880 | if (WRITE(GC_stderr, buf, strlen(buf)) < 0) ABORT("write to stderr failed"); |
---|
881 | } |
---|
882 | |
---|
883 | void GC_err_puts(s) |
---|
884 | GC_CONST char *s; |
---|
885 | { |
---|
886 | if (WRITE(GC_stderr, s, strlen(s)) < 0) ABORT("write to stderr failed"); |
---|
887 | } |
---|
888 | |
---|
889 | #if defined(LINUX) && !defined(SMALL_CONFIG) |
---|
890 | void GC_err_write(buf, len) |
---|
891 | GC_CONST char *buf; |
---|
892 | size_t len; |
---|
893 | { |
---|
894 | if (WRITE(GC_stderr, buf, len) < 0) ABORT("write to stderr failed"); |
---|
895 | } |
---|
896 | #endif |
---|
897 | |
---|
898 | # if defined(__STDC__) || defined(__cplusplus) |
---|
899 | void GC_default_warn_proc(char *msg, GC_word arg) |
---|
900 | # else |
---|
901 | void GC_default_warn_proc(msg, arg) |
---|
902 | char *msg; |
---|
903 | GC_word arg; |
---|
904 | # endif |
---|
905 | { |
---|
906 | GC_err_printf1(msg, (unsigned long)arg); |
---|
907 | } |
---|
908 | |
---|
909 | GC_warn_proc GC_current_warn_proc = GC_default_warn_proc; |
---|
910 | |
---|
911 | # if defined(__STDC__) || defined(__cplusplus) |
---|
912 | GC_warn_proc GC_set_warn_proc(GC_warn_proc p) |
---|
913 | # else |
---|
914 | GC_warn_proc GC_set_warn_proc(p) |
---|
915 | GC_warn_proc p; |
---|
916 | # endif |
---|
917 | { |
---|
918 | GC_warn_proc result; |
---|
919 | |
---|
920 | LOCK(); |
---|
921 | result = GC_current_warn_proc; |
---|
922 | GC_current_warn_proc = p; |
---|
923 | UNLOCK(); |
---|
924 | return(result); |
---|
925 | } |
---|
926 | |
---|
927 | |
---|
928 | #ifndef PCR |
---|
929 | void GC_abort(msg) |
---|
930 | GC_CONST char * msg; |
---|
931 | { |
---|
932 | # if defined(MSWIN32) |
---|
933 | (void) MessageBoxA(NULL, msg, "Fatal error in gc", MB_ICONERROR|MB_OK); |
---|
934 | DebugBreak(); |
---|
935 | # else |
---|
936 | GC_err_printf1("%s\n", msg); |
---|
937 | # endif |
---|
938 | if (GETENV("GC_LOOP_ON_ABORT") != NULL) { |
---|
939 | /* In many cases it's easier to debug a running process. */ |
---|
940 | /* It's arguably nicer to sleep, but that makes it harder */ |
---|
941 | /* to look at the thread if the debugger doesn't know much */ |
---|
942 | /* about threads. */ |
---|
943 | for(;;) {} |
---|
944 | } |
---|
945 | # ifdef MSWIN32 |
---|
946 | DebugBreak(); |
---|
947 | # else |
---|
948 | (void) abort(); |
---|
949 | # endif |
---|
950 | } |
---|
951 | #endif |
---|
952 | |
---|
953 | #ifdef NEED_CALLINFO |
---|
954 | |
---|
955 | #ifdef HAVE_BUILTIN_BACKTRACE |
---|
956 | # include <execinfo.h> |
---|
957 | # ifdef LINUX |
---|
958 | # include <unistd.h> |
---|
959 | # endif |
---|
960 | #endif |
---|
961 | |
---|
962 | void GC_print_callers (info) |
---|
963 | struct callinfo info[NFRAMES]; |
---|
964 | { |
---|
965 | register int i; |
---|
966 | |
---|
967 | # if NFRAMES == 1 |
---|
968 | GC_err_printf0("\tCaller at allocation:\n"); |
---|
969 | # else |
---|
970 | GC_err_printf0("\tCall chain at allocation:\n"); |
---|
971 | # endif |
---|
972 | for (i = 0; i < NFRAMES; i++) { |
---|
973 | if (info[i].ci_pc == 0) break; |
---|
974 | # if NARGS > 0 |
---|
975 | { |
---|
976 | int j; |
---|
977 | |
---|
978 | GC_err_printf0("\t\targs: "); |
---|
979 | for (j = 0; j < NARGS; j++) { |
---|
980 | if (j != 0) GC_err_printf0(", "); |
---|
981 | GC_err_printf2("%d (0x%X)", ~(info[i].ci_arg[j]), |
---|
982 | ~(info[i].ci_arg[j])); |
---|
983 | } |
---|
984 | GC_err_printf0("\n"); |
---|
985 | } |
---|
986 | # endif |
---|
987 | # if defined(HAVE_BUILTIN_BACKTRACE) && !defined(REDIRECT_MALLOC) |
---|
988 | /* Unfortunately backtrace_symbols calls malloc, which makes */ |
---|
989 | /* it dangersous if that has been redirected. */ |
---|
990 | { |
---|
991 | char **sym_name = |
---|
992 | backtrace_symbols((void **)(&(info[i].ci_pc)), 1); |
---|
993 | char *name = sym_name[0]; |
---|
994 | GC_bool found_it = (strchr(name, '(') != 0); |
---|
995 | FILE *pipe; |
---|
996 | # ifdef LINUX |
---|
997 | if (!found_it) { |
---|
998 | # define EXE_SZ 100 |
---|
999 | static char exe_name[EXE_SZ]; |
---|
1000 | # define CMD_SZ 200 |
---|
1001 | char cmd_buf[CMD_SZ]; |
---|
1002 | # define RESULT_SZ 200 |
---|
1003 | static char result_buf[RESULT_SZ]; |
---|
1004 | size_t result_len; |
---|
1005 | static GC_bool found_exe_name = FALSE; |
---|
1006 | static GC_bool will_fail = FALSE; |
---|
1007 | int ret_code; |
---|
1008 | /* Unfortunately, this is the common case for the */ |
---|
1009 | /* main executable. */ |
---|
1010 | /* Try to get it via a hairy and expensive scheme. */ |
---|
1011 | /* First we get the name of the executable: */ |
---|
1012 | if (will_fail) goto out; |
---|
1013 | if (!found_exe_name) { |
---|
1014 | ret_code = readlink("/proc/self/exe", exe_name, EXE_SZ); |
---|
1015 | if (ret_code < 0 || ret_code >= EXE_SZ || exe_name[0] != '/') { |
---|
1016 | will_fail = TRUE; /* Dont try again. */ |
---|
1017 | goto out; |
---|
1018 | } |
---|
1019 | exe_name[ret_code] = '\0'; |
---|
1020 | found_exe_name = TRUE; |
---|
1021 | } |
---|
1022 | /* Then we use popen to start addr2line -e <exe> <addr> */ |
---|
1023 | /* There are faster ways to do this, but hopefully this */ |
---|
1024 | /* isn't time critical. */ |
---|
1025 | sprintf(cmd_buf, "/usr/bin/addr2line -e %s 0x%lx", exe_name, |
---|
1026 | (unsigned long)info[i].ci_pc); |
---|
1027 | pipe = popen(cmd_buf, "r"); |
---|
1028 | if (pipe < 0 || fgets(result_buf, RESULT_SZ, pipe) == 0) { |
---|
1029 | will_fail = TRUE; |
---|
1030 | goto out; |
---|
1031 | } |
---|
1032 | result_len = strlen(result_buf); |
---|
1033 | if (result_buf[result_len - 1] == '\n') --result_len; |
---|
1034 | if (result_buf[0] == '?' |
---|
1035 | || result_buf[result_len-2] == ':' |
---|
1036 | && result_buf[result_len-1] == '0') |
---|
1037 | goto out; |
---|
1038 | if (result_len < RESULT_SZ - 25) { |
---|
1039 | /* Add in hex address */ |
---|
1040 | sprintf(result_buf + result_len, " [0x%lx]", |
---|
1041 | (unsigned long)info[i].ci_pc); |
---|
1042 | } |
---|
1043 | name = result_buf; |
---|
1044 | pclose(pipe); |
---|
1045 | out: |
---|
1046 | } |
---|
1047 | # endif |
---|
1048 | GC_err_printf1("\t\t%s\n", name); |
---|
1049 | free(sym_name); |
---|
1050 | } |
---|
1051 | # else |
---|
1052 | GC_err_printf1("\t\t##PC##= 0x%lx\n", info[i].ci_pc); |
---|
1053 | # endif |
---|
1054 | } |
---|
1055 | } |
---|
1056 | |
---|
1057 | #endif /* SAVE_CALL_CHAIN */ |
---|
1058 | |
---|
1059 | /* Needed by SRC_M3, gcj, and should perhaps be the official interface */ |
---|
1060 | /* to GC_dont_gc. */ |
---|
1061 | void GC_enable() |
---|
1062 | { |
---|
1063 | GC_dont_gc--; |
---|
1064 | } |
---|
1065 | |
---|
1066 | void GC_disable() |
---|
1067 | { |
---|
1068 | GC_dont_gc++; |
---|
1069 | } |
---|
1070 | |
---|
1071 | #if !defined(NO_DEBUGGING) |
---|
1072 | |
---|
1073 | void GC_dump() |
---|
1074 | { |
---|
1075 | GC_printf0("***Static roots:\n"); |
---|
1076 | GC_print_static_roots(); |
---|
1077 | GC_printf0("\n***Heap sections:\n"); |
---|
1078 | GC_print_heap_sects(); |
---|
1079 | GC_printf0("\n***Free blocks:\n"); |
---|
1080 | GC_print_hblkfreelist(); |
---|
1081 | GC_printf0("\n***Blocks in use:\n"); |
---|
1082 | GC_print_block_list(); |
---|
1083 | } |
---|
1084 | |
---|
1085 | #endif /* NO_DEBUGGING */ |
---|