2 * stop and copy GC based on Cheney's algorithm
6 * This software is part of the SBCL system. See the README file for
9 * This software is derived from the CMU CL system, which was
10 * written at Carnegie Mellon University and released into the
11 * public domain. The software is in the public domain and is
12 * provided with absolutely no warranty. See the COPYING and CREDITS
13 * files for more information.
18 #include <sys/resource.h>
21 #include "genesis/sbcl.h"
26 #include "interrupt.h"
30 #include "genesis/static-symbols.h"
31 #include "genesis/primitive-objects.h"
35 #include "private-cons.inc"
36 #include "pseudo-atomic.h"
37 #include "genesis/gc-tables.h" // for leaf_obj_widetag_p
39 /* So you need to debug? */
42 #define DEBUG_SPACE_PREDICATES
43 #define DEBUG_SCAVENGE_VERBOSE
44 #define DEBUG_COPY_VERBOSE
48 lispobj
*from_space_free_pointer
;
51 lispobj
*new_space_free_pointer
;
53 lispobj
*current_dynamic_space
;
55 /* This does nothing. It's only to satisfy a reference from gc-common. */
56 char gc_coalesce_string_literals
= 0;
58 boolean gc_active_p
= 0;
60 static void scavenge_newspace(void);
63 /* collecting garbage */
67 tv_diff(struct timeval
*x
, struct timeval
*y
)
69 return (((double) x
->tv_sec
+ (double) x
->tv_usec
* 1.0e-6) -
70 ((double) y
->tv_sec
+ (double) y
->tv_usec
* 1.0e-6));
75 gc_general_alloc(__attribute__((unused
)) void* ignore
,
77 __attribute__((unused
)) int page_type
) {
78 lispobj
*new=new_space_free_pointer
;
79 new_space_free_pointer
+=(bytes
/N_WORD_BYTES
);
83 lispobj
copy_unboxed_object(lispobj object
, sword_t nwords
) {
84 return gc_copy_object(object
, nwords
, 0, 0);
86 lispobj
copy_potential_large_object(lispobj object
, sword_t nwords
,
87 __attribute__((unused
)) void* region
,
88 __attribute__((unused
)) int page_type
) {
89 return gc_copy_object(object
, nwords
, 0, 0);
93 static void verify_range(int purified
, lispobj
*base
, lispobj
*end
);
96 fprintf(stderr
, " R/O = %10p:%10p\n", (void*)READ_ONLY_SPACE_START
, read_only_space_free_pointer
);
97 fprintf(stderr
, " static = %10p:%10p\n", (void*)STATIC_SPACE_START
, static_space_free_pointer
);
98 fprintf(stderr
, " dynamic = %10p:%10p\n", current_dynamic_space
, dynamic_space_free_pointer
);
99 struct thread
* th
= all_threads
;
100 fprintf(stderr
, " stack = %10p:%10p\n",
101 th
->control_stack_start
,
102 access_control_stack_pointer(th
));
104 void verify_heap(int purified
)
107 verify_range(purified
, (lispobj
*)READ_ONLY_SPACE_START
, read_only_space_free_pointer
);
108 verify_range(purified
, (lispobj
*)STATIC_SPACE_START
, static_space_free_pointer
);
110 verify_range(0, current_dynamic_space
, dynamic_space_free_pointer
);
114 /* Note: The generic GC interface we're implementing passes us a
115 * last_generation argument. That's meaningless for us, since we're
116 * not a generational GC. So we ignore it. */
119 collect_garbage(generation_index_t ignore
)
122 struct timeval start_tv
, stop_tv
;
123 struct rusage start_rusage
, stop_rusage
;
124 double real_time
, system_time
, user_time
;
125 double percent_retained
, gc_rate
;
126 unsigned long size_discarded
;
128 unsigned long size_retained
;
129 lispobj
*current_static_space_free_pointer
;
131 struct thread
*th
=get_sb_vm_thread();
134 printf("[Collecting garbage ... \n");
136 getrusage(RUSAGE_SELF
, &start_rusage
);
137 gettimeofday(&start_tv
, (struct timezone
*) 0);
142 /* it's possible that signals are blocked already if this was called
143 * from a signal handler (e.g. with the sigsegv gc_trigger stuff) */
144 block_blockable_signals(&old
);
146 current_static_space_free_pointer
= static_space_free_pointer
;
148 /* Set up from space and new space pointers. */
150 // ++n_gcs; fprintf(stderr, "[%d] pre-verify:\n", n_gcs); verify_heap(0);
151 from_space
= current_dynamic_space
;
152 from_space_free_pointer
= get_alloc_pointer();
155 fprintf(stderr
,"from_space = %lx\n",
156 (unsigned long) current_dynamic_space
);
158 if (current_dynamic_space
== (lispobj
*) DYNAMIC_0_SPACE_START
)
159 new_space
= (lispobj
*)DYNAMIC_1_SPACE_START
;
160 else if (current_dynamic_space
== (lispobj
*) DYNAMIC_1_SPACE_START
)
161 new_space
= (lispobj
*) DYNAMIC_0_SPACE_START
;
163 lose("GC lossage. Current dynamic space is bogus!");
165 new_space_free_pointer
= new_space
;
167 /* Scavenge all of the roots. */
169 printf("Scavenging interrupt contexts ...\n");
171 scavenge_interrupt_contexts(th
);
174 printf("Scavenging interrupt handlers ...\n");
176 scavenge(lisp_sig_handlers
, NSIG
);
179 printf("Scavenging the control stack ...\n");
181 scavenge_control_stack(th
);
183 scav_binding_stack((lispobj
*)th
->binding_stack_start
,
184 (lispobj
*)get_binding_stack_pointer(th
),
187 heap_scavenge(((lispobj
*)STATIC_SPACE_START
),
188 current_static_space_free_pointer
);
189 scavenge(&lisp_package_vector
, 1);
191 /* Scavenge newspace. */
193 printf("Scavenging new space (%d bytes) ...\n",
194 (int)((new_space_free_pointer
- new_space
) * sizeof(lispobj
)));
199 #if defined(DEBUG_PRINT_GARBAGE)
200 print_garbage(from_space
, from_space_free_pointer
);
203 scan_binding_stack();
205 smash_weak_pointers();
206 gc_dispose_private_pages();
207 cull_weak_hash_tables(weak_ht_alivep_funs
);
211 printf("Flipping spaces ...\n");
214 /* Maybe FIXME: it's possible that we could significantly reduce
215 * RSS by zeroing the from_space or madvise(MADV_DONTNEED) or
216 * similar os-dependent tricks here */
217 os_zero((os_vm_address_t
) from_space
,
218 (os_vm_size_t
) dynamic_space_size
);
220 current_dynamic_space
= new_space
;
221 set_alloc_pointer((lispobj
)new_space_free_pointer
);
224 size_discarded
= (from_space_free_pointer
- from_space
) * sizeof(lispobj
);
226 size_retained
= (new_space_free_pointer
- new_space
) * sizeof(lispobj
);
228 os_flush_icache((os_vm_address_t
)new_space
, size_retained
);
232 printf("Zeroing empty part of control stack ...\n");
234 scrub_control_stack();
235 set_auto_gc_trigger(size_retained
+bytes_consed_between_gcs
);
236 thread_sigmask(SIG_SETMASK
, &old
, 0);
239 // fprintf(stderr, "post-verify:\n"); verify_heap(0);
242 gettimeofday(&stop_tv
, (struct timezone
*) 0);
243 getrusage(RUSAGE_SELF
, &stop_rusage
);
247 percent_retained
= (((float) size_retained
) /
248 ((float) size_discarded
)) * 100.0;
250 printf("Total of %ld bytes out of %ld bytes retained (%3.2f%%).\n",
251 size_retained
, size_discarded
, percent_retained
);
253 real_time
= tv_diff(&stop_tv
, &start_tv
);
254 user_time
= tv_diff(&stop_rusage
.ru_utime
, &start_rusage
.ru_utime
);
255 system_time
= tv_diff(&stop_rusage
.ru_stime
, &start_rusage
.ru_stime
);
257 printf("Statistics: %10.2fs real, %10.2fs user, %10.2fs system.\n",
258 real_time
, user_time
, system_time
);
260 gc_rate
= ((float) size_retained
/ (float) (1<<20)) / real_time
;
262 printf("%10.2f M bytes/sec collected.\n", gc_rate
);
270 scavenge_newspace(void)
272 lispobj
*here
, *next
;
277 /* printf("here=%lx, new_space_free_pointer=%lx\n",
278 here,new_space_free_pointer); */
279 next
= new_space_free_pointer
;
280 heap_scavenge(here
, next
);
282 } while (new_space_free_pointer
> here
||
283 (test_weak_triggers(0, 0) && new_space_free_pointer
> here
));
284 /* printf("done with newspace\n"); */
290 print_garbage(lispobj
*from_space
, lispobj
*from_space_free_pointer
)
293 int total_words_not_copied
;
295 printf("Scanning from space ...\n");
297 total_words_not_copied
= 0;
299 while (start
< from_space_free_pointer
) {
301 int forwardp
, type
, nwords
;
305 forwardp
= is_lisp_pointer(object
) && new_space_p(object
);
311 tag
= lowtag_of(object
);
314 case LIST_POINTER_LOWTAG
:
317 case INSTANCE_POINTER_LOWTAG
:
318 printf("Don't know about instances yet!\n");
321 case FUN_POINTER_LOWTAG
:
324 case OTHER_POINTER_LOWTAG
:
325 pointer
= native_pointer(object
);
327 type
= header_widetag(header
);
328 nwords
= (sizetab
[type
])(pointer
);
330 default: nwords
=1; /* shut yer whinging, gcc */
333 type
= header_widetag(object
);
334 nwords
= (sizetab
[type
])(start
);
335 total_words_not_copied
+= nwords
;
336 printf("%4d words not copied at 0x%16lx; ",
337 nwords
, (unsigned long) start
);
338 printf("Header word is 0x%08x\n",
339 (unsigned int) object
);
343 printf("%d total words not copied.\n", total_words_not_copied
);
347 search_dynamic_space(void *pointer
)
349 lispobj
*start
= (lispobj
*) current_dynamic_space
;
350 lispobj
*end
= get_alloc_pointer();
351 if ((pointer
< (void *)start
) || (pointer
>= (void *)end
))
353 return gc_search_space(start
, pointer
);
356 /* initialization. if gc_init can be moved to after core load, we could
357 * combine these two functions */
365 /* noise to manipulate the gc trigger stuff */
367 /* Functions that substantially change the dynamic space free pointer
368 * (collect_garbage, purify) are responsible also for resetting the
370 void set_auto_gc_trigger(os_vm_size_t dynamic_usage
)
372 os_vm_address_t addr
;
375 addr
= os_round_up_to_page((os_vm_address_t
)current_dynamic_space
377 if (addr
< (os_vm_address_t
)get_alloc_pointer())
378 lose("set_auto_gc_trigger: tried to set gc trigger too low! (%ld < 0x%08lx)",
379 (unsigned long)dynamic_usage
,
380 (unsigned long)((os_vm_address_t
)get_alloc_pointer()
381 - (os_vm_address_t
)current_dynamic_space
));
383 if (dynamic_usage
> dynamic_space_size
)
384 lose("set_auto_gc_trigger: tried to set gc trigger too high! (0x%08lx)",
385 (unsigned long)dynamic_usage
);
386 length
= os_trunc_size_to_page(dynamic_space_size
- dynamic_usage
);
388 // range has to fall entirely within either semispace
389 uword_t semispace_0_end
= DYNAMIC_0_SPACE_START
+ dynamic_space_size
;
390 uword_t semispace_1_end
= DYNAMIC_1_SPACE_START
+ dynamic_space_size
;
391 uword_t end
= (uword_t
)addr
+ length
- 1;
392 if (((uword_t
)addr
>= DYNAMIC_0_SPACE_START
&& end
< semispace_0_end
) ||
393 ((uword_t
)addr
>= DYNAMIC_1_SPACE_START
&& end
< semispace_1_end
)) {
394 #if defined(SUNOS) || defined(SOLARIS)
395 os_deallocate(addr
, length
);
397 os_protect(addr
, length
, 0);
400 lose("auto_gc_trigger can't protect %p..%p (not owned)\n",
401 (void*)addr
, (char*)end
-1);
403 current_auto_gc_trigger
= (lispobj
*)addr
;
406 void clear_auto_gc_trigger(void)
408 os_vm_address_t addr
;
411 if (current_auto_gc_trigger
== NULL
)
414 addr
= (os_vm_address_t
)current_auto_gc_trigger
;
415 length
= dynamic_space_size
+ (os_vm_address_t
)current_dynamic_space
- addr
;
417 #if defined(SUNOS) || defined(SOLARIS)
418 /* don't want to force whole space into swapping mode... */
419 os_validate(NOT_MOVABLE
, addr
, length
);
421 os_protect(addr
, length
, OS_VM_PROT_ALL
);
424 current_auto_gc_trigger
= NULL
;
428 gc_trigger_hit(void *addr
)
430 if (current_auto_gc_trigger
== NULL
)
433 return (addr
>= (void *)current_auto_gc_trigger
&&
434 (char*)addr
<((char *)current_dynamic_space
+ dynamic_space_size
));
439 cheneygc_handle_wp_violation(os_context_t
*context
, void *addr
)
441 if(!foreign_function_call_active
&& gc_trigger_hit(addr
)){
442 struct thread
*thread
=get_sb_vm_thread();
443 clear_auto_gc_trigger();
444 /* Don't flood the system with interrupts if the need to gc is
445 * already noted. This can happen for example when SUB-GC
446 * allocates or after a gc triggered in a WITHOUT-GCING. */
447 if (SymbolValue(GC_PENDING
,thread
) == NIL
) {
448 if (SymbolValue(GC_INHIBIT
,thread
) == NIL
) {
449 if (arch_pseudo_atomic_atomic(context
)) {
450 /* set things up so that GC happens when we finish
452 SetSymbolValue(GC_PENDING
,T
,thread
);
453 arch_set_pseudo_atomic_interrupted(context
);
454 maybe_save_gc_mask_and_block_deferrables
455 (os_context_sigmask_addr(context
));
460 SetSymbolValue(GC_PENDING
,T
,thread
);
468 void gc_show_pte(lispobj obj
)
470 printf("unimplemented\n");
473 sword_t
scav_code_blob(lispobj
*where
, lispobj header
)
475 struct code
*code
= (struct code
*) where
;
476 sword_t n_header_words
= code_header_words(code
);
478 /* Scavenge the boxed section of the code data block. */
479 scavenge(where
+ 2, n_header_words
- 2);
480 /* And scavenge any 'self' pointers pointing outside of the object */
481 for_each_simple_fun(i
, fun
, code
, 1, {
482 if (simplefun_is_wrapped(fun
)) {
483 lispobj target_fun
= fun_taggedptr_from_self(fun
->self
);
484 lispobj
new = target_fun
;
486 if (new != target_fun
) fun
->self
= fun_self_from_taggedptr(new);
490 return code_total_nwords(code
);
494 static boolean
in_stack_range_p(lispobj ptr
)
496 struct thread
* th
= all_threads
;
497 return (ptr
>= (uword_t
)th
->control_stack_start
&&
498 ptr
< (uword_t
)access_control_stack_pointer(th
));
501 static boolean
valid_space_p(int purified
, lispobj ptr
)
503 if ((ptr
>= READ_ONLY_SPACE_START
&& ptr
< (uword_t
)read_only_space_free_pointer
) ||
504 (ptr
>= STATIC_SPACE_START
&& ptr
< (uword_t
)static_space_free_pointer
) ||
506 ((ptr
>= (uword_t
)current_dynamic_space
&& ptr
< (uword_t
)dynamic_space_free_pointer
) ||
507 in_stack_range_p(ptr
))))
512 void check_ptr(int purified
, lispobj
* where
, lispobj ptr
)
514 if (!is_lisp_pointer(ptr
)) return;
516 if (!valid_space_p(purified
, ptr
)) {
517 fprintf(stderr
, "Sus' pointer %p @ %p outside expected ranges\n",
522 // must be properly tagged
523 if (lowtag_of(ptr
) == LIST_POINTER_LOWTAG
) {
524 gc_assert(is_cons_half(CONS(ptr
)->car
));
525 gc_assert(is_cons_half(CONS(ptr
)->cdr
));
527 int widetag
= widetag_of(native_pointer(ptr
));
528 if (LOWTAG_FOR_WIDETAG(widetag
) != lowtag_of(ptr
) && !in_stack_range_p(ptr
))
529 lose("Widetag/lowtag mismatch on %p", (void*)ptr
);
533 #define CHECK_PTR(x,y) check_ptr(purified,x,y)
536 verify_range(int purified
, lispobj
*base
, lispobj
*end
)
538 lispobj
* where
= base
;
541 for ( ; where
< end
; where
+= object_size(where
) ) {
542 if (is_cons_half(*where
)) {
543 CHECK_PTR(where
+0, where
[0]);
544 CHECK_PTR(where
+1, where
[1]);
545 } else switch (widetag_of(where
)) {
546 case INSTANCE_WIDETAG
:
547 layout
= instance_layout(where
);
549 gc_assert(layoutp(layout
));
550 len
= instance_length(*where
);
551 struct bitmap bitmap
= get_layout_bitmap(LAYOUT(layout
));
552 for (i
=0; i
<len
; ++i
)
553 if (bitmap_logbitp(i
, bitmap
)) CHECK_PTR(where
+i
+1, where
[1+i
]);
556 case CODE_HEADER_WIDETAG
:
557 len
= code_header_words((struct code
*)where
);
558 for(i
=2; i
<len
; ++i
) CHECK_PTR(where
+i
, where
[i
]);
561 CHECK_PTR(where
+1, where
[1]);
562 CHECK_PTR(where
+2, where
[2]);
563 CHECK_PTR(where
+3, decode_fdefn_rawfun((struct fdefn
*)where
));
565 case CLOSURE_WIDETAG
:
566 case FUNCALLABLE_INSTANCE_WIDETAG
:
567 // Scan the closure's trampoline word.
568 CHECK_PTR(where
+1, fun_taggedptr_from_self(where
[1]));
569 len
= SHORT_BOXED_NWORDS(*where
);
570 for(i
=2; i
<=len
; ++i
) CHECK_PTR(where
+i
, where
[i
]);
573 if (!leaf_obj_widetag_p(widetag_of(where
))) {
574 int size
= sizetab
[widetag_of(where
)](where
);
575 for(i
=1; i
<size
; ++i
) CHECK_PTR(where
+i
, where
[i
]);
581 void dump_space_to_file(lispobj
* where
, lispobj
* limit
, char* pathname
)
584 f
= fopen(pathname
, "w");
585 while (where
< limit
) {
586 fprintf(f
, "%p: %x\n", where
, *where
);
587 where
+= object_size(where
);