Branch data Line data Source code
1 : : // This file is a part of Julia. License is MIT: https://julialang.org/license
2 : :
3 : : #include "gc.h"
4 : : #include <inttypes.h>
5 : : #include <stdio.h>
6 : :
7 : : // re-include assert.h without NDEBUG,
8 : : // so that we can always use the assert macro in this file
9 : : // for use under their respective enable flags
10 : : #undef NDEBUG
11 : : #include "julia_assert.h"
12 : :
13 : : #ifdef __cplusplus
14 : : extern "C" {
15 : : #endif
16 : :
17 : : // Useful function in debugger to find page metadata
18 : 0 : jl_gc_pagemeta_t *jl_gc_page_metadata(void *data)
19 : : {
20 : 0 : return page_metadata(data);
21 : : }
22 : :
23 : : // Find the memory block in the pool that owns the byte pointed to by p.
24 : : // For end of object pointer (which is always the case for pointer to a
25 : : // singleton object), this usually returns the same pointer which points to
26 : : // the next object but it can also return NULL if the pointer is pointing to
27 : : // the end of the page.
28 : 0 : JL_DLLEXPORT jl_taggedvalue_t *jl_gc_find_taggedvalue_pool(char *p, size_t *osize_p)
29 : : {
30 [ # # ]: 0 : if (!page_metadata(p))
31 : : // Not in the pool
32 : 0 : return NULL;
33 : 0 : struct jl_gc_metadata_ext info = page_metadata_ext(p);
34 : 0 : char *page_begin = gc_page_data(p) + GC_PAGE_OFFSET;
35 : : // In the page header
36 [ # # ]: 0 : if (p < page_begin)
37 : 0 : return NULL;
38 : 0 : size_t ofs = p - page_begin;
39 : : // Check if this is a free page
40 [ # # ]: 0 : if (!(info.pagetable0->allocmap[info.pagetable0_i32] & (uint32_t)(1 << info.pagetable0_i)))
41 : 0 : return NULL;
42 : 0 : int osize = info.meta->osize;
43 : : // Shouldn't be needed, just in case
44 [ # # ]: 0 : if (osize == 0)
45 : 0 : return NULL;
46 : 0 : char *tag = (char*)p - ofs % osize;
47 : : // Points to an "object" that gets into the next page
48 [ # # ]: 0 : if (tag + osize > gc_page_data(p) + GC_PAGE_SZ)
49 : 0 : return NULL;
50 [ # # ]: 0 : if (osize_p)
51 : 0 : *osize_p = osize;
52 : 0 : return (jl_taggedvalue_t*)tag;
53 : : }
54 : :
55 : : // mark verification
56 : : #ifdef GC_VERIFY
57 : : jl_value_t *lostval = NULL;
58 : : static arraylist_t lostval_parents;
59 : : static arraylist_t lostval_parents_done;
60 : : int gc_verifying;
61 : :
62 : : void add_lostval_parent(jl_value_t *parent)
63 : : {
64 : : for(int i = 0; i < lostval_parents_done.len; i++) {
65 : : if ((jl_value_t*)lostval_parents_done.items[i] == parent)
66 : : return;
67 : : }
68 : : for(int i = 0; i < lostval_parents.len; i++) {
69 : : if ((jl_value_t*)lostval_parents.items[i] == parent)
70 : : return;
71 : : }
72 : : arraylist_push(&lostval_parents, parent);
73 : : }
74 : :
75 : : /*
76 : : How to debug a missing write barrier :
77 : : (or rather how I do it, if you know of a better way update this)
78 : : First, reproduce it with GC_VERIFY. It does change the allocation profile so if the error
79 : : is rare enough this may not be straightforward. If the backtracking goes well you should know
80 : : which object and which of its slots was written to without being caught by the write
81 : : barrier. Most times this allows you to take a guess. If this type of object is modified
82 : : by C code directly, look for missing jl_gc_wb() on pointer updates. Be aware that there are
83 : : innocent looking functions which allocate (and thus trigger marking) only on special cases.
84 : :
85 : : If you can't find it, you can try the following :
86 : : - Ensure that should_timeout() is deterministic instead of clock based.
87 : : - Once you have a completely deterministic program which crashes on gc_verify, the addresses
88 : : should stay constant between different runs (with same binary, same environment ...).
89 : : Do not forget to turn off ASLR (linux: echo 0 > /proc/sys/kernel/randomize_va_space).
90 : : At this point you should be able to run under gdb and use a hw watch to look for writes
91 : : at the exact addr of the slot (use something like watch *slot_addr if *slot_addr == val).
92 : : - If it went well you are now stopped at the exact point the problem is happening.
93 : : Backtraces in JIT'd code wont work for me (but I'm not sure they should) so in that
94 : : case you can try to jl_throw(something) from gdb.
95 : : */
96 : : // this does not yet detect missing writes from marked to marked_noesc
97 : : // the error is caught at the first long collection
98 : : static arraylist_t bits_save[4];
99 : :
100 : : static void gc_clear_mark_page(jl_gc_pagemeta_t *pg, int bits)
101 : : {
102 : : jl_ptls_t ptls2 = jl_all_tls_states[pg->thread_n];
103 : : jl_gc_pool_t *pool = &ptls2->heap.norm_pools[pg->pool_n];
104 : : jl_taggedvalue_t *pv = (jl_taggedvalue_t*)(pg->data + GC_PAGE_OFFSET);
105 : : char *lim = (char*)pv + GC_PAGE_SZ - GC_PAGE_OFFSET - pool->osize;
106 : : while ((char*)pv <= lim) {
107 : : if (!gc_verifying)
108 : : arraylist_push(&bits_save[pv->bits.gc], pv);
109 : : pv->bits.gc = bits;
110 : : pv = (jl_taggedvalue_t*)((char*)pv + pool->osize);
111 : : }
112 : : }
113 : :
114 : : static void gc_clear_mark_pagetable0(pagetable0_t *pagetable0, int bits)
115 : : {
116 : : for (int pg_i = 0; pg_i < REGION0_PG_COUNT / 32; pg_i++) {
117 : : uint32_t line = pagetable0->allocmap[pg_i];
118 : : if (line) {
119 : : for (int j = 0; j < 32; j++) {
120 : : if ((line >> j) & 1) {
121 : : gc_clear_mark_page(pagetable0->meta[pg_i * 32 + j], bits);
122 : : }
123 : : }
124 : : }
125 : : }
126 : : }
127 : :
128 : : static void gc_clear_mark_pagetable1(pagetable1_t *pagetable1, int bits)
129 : : {
130 : : for (int pg_i = 0; pg_i < REGION1_PG_COUNT / 32; pg_i++) {
131 : : uint32_t line = pagetable1->allocmap0[pg_i];
132 : : if (line) {
133 : : for (int j = 0; j < 32; j++) {
134 : : if ((line >> j) & 1) {
135 : : gc_clear_mark_pagetable0(pagetable1->meta0[pg_i * 32 + j], bits);
136 : : }
137 : : }
138 : : }
139 : : }
140 : : }
141 : :
142 : : static void gc_clear_mark_pagetable(int bits)
143 : : {
144 : : for (int pg_i = 0; pg_i < (REGION2_PG_COUNT + 31) / 32; pg_i++) {
145 : : uint32_t line = memory_map.allocmap1[pg_i];
146 : : if (line) {
147 : : for (int j = 0; j < 32; j++) {
148 : : if ((line >> j) & 1) {
149 : : gc_clear_mark_pagetable1(memory_map.meta1[pg_i * 32 + j], bits);
150 : : }
151 : : }
152 : : }
153 : : }
154 : : }
155 : : // set all mark bits to bits
156 : : // record the state of the region and can replay it in restore()
157 : : // restore _must_ be called as this will overwrite parts of the
158 : : // freelist in pools
159 : : static void clear_mark(int bits)
160 : : {
161 : : if (!gc_verifying) {
162 : : for (int i = 0; i < 4; i++) {
163 : : bits_save[i].len = 0;
164 : : }
165 : : }
166 : : bigval_t *v;
167 : : for (int i = 0;i < jl_n_threads;i++) {
168 : : v = jl_all_tls_states[i]->heap.big_objects;
169 : : while (v != NULL) {
170 : : void *gcv = &v->header;
171 : : if (!gc_verifying)
172 : : arraylist_push(&bits_save[v->bits.gc], gcv);
173 : : v->bits.gc = bits;
174 : : v = v->next;
175 : : }
176 : : }
177 : :
178 : : v = big_objects_marked;
179 : : while (v != NULL) {
180 : : void *gcv = &v->header;
181 : : if (!gc_verifying)
182 : : arraylist_push(&bits_save[v->bits.gc], gcv);
183 : : v->bits.gc = bits;
184 : : v = v->next;
185 : : }
186 : :
187 : : gc_clear_mark_pagetable(bits);
188 : : }
189 : :
190 : : static void restore(void)
191 : : {
192 : : for (int b = 0; b < 4; b++) {
193 : : for (int i = 0; i < bits_save[b].len; i++) {
194 : : ((jl_taggedvalue_t*)bits_save[b].items[i])->bits.gc = b;
195 : : }
196 : : }
197 : : }
198 : :
199 : : static void gc_verify_track(jl_ptls_t ptls)
200 : : {
201 : : jl_gc_mark_cache_t *gc_cache = &ptls->gc_cache;
202 : : do {
203 : : jl_gc_mark_sp_t sp;
204 : : gc_mark_sp_init(gc_cache, &sp);
205 : : arraylist_push(&lostval_parents_done, lostval);
206 : : jl_safe_printf("Now looking for %p =======\n", lostval);
207 : : clear_mark(GC_CLEAN);
208 : : gc_mark_queue_all_roots(ptls, &sp);
209 : : gc_mark_queue_finlist(gc_cache, &sp, &to_finalize, 0);
210 : : for (int i = 0;i < jl_n_threads;i++) {
211 : : jl_ptls_t ptls2 = jl_all_tls_states[i];
212 : : gc_mark_queue_finlist(gc_cache, &sp, &ptls2->finalizers, 0);
213 : : }
214 : : gc_mark_queue_finlist(gc_cache, &sp, &finalizer_list_marked, 0);
215 : : gc_mark_loop(ptls, sp);
216 : : if (lostval_parents.len == 0) {
217 : : jl_safe_printf("Could not find the missing link. We missed a toplevel root. This is odd.\n");
218 : : break;
219 : : }
220 : : jl_value_t *lostval_parent = NULL;
221 : : for(int i = 0; i < lostval_parents.len; i++) {
222 : : lostval_parent = (jl_value_t*)lostval_parents.items[i];
223 : : int clean_len = bits_save[GC_CLEAN].len;
224 : : for(int j = 0; j < clean_len + bits_save[GC_OLD].len; j++) {
225 : : void *p = bits_save[j >= clean_len ? GC_OLD : GC_CLEAN].items[j >= clean_len ? j - clean_len : j];
226 : : if (jl_valueof(p) == lostval_parent) {
227 : : lostval = lostval_parent;
228 : : lostval_parent = NULL;
229 : : break;
230 : : }
231 : : }
232 : : if (lostval_parent != NULL) break;
233 : : }
234 : : if (lostval_parent == NULL) { // all parents of lostval were also scheduled for deletion
235 : : lostval = (jl_value_t*)arraylist_pop(&lostval_parents);
236 : : }
237 : : else {
238 : : jl_safe_printf("Missing write barrier found !\n");
239 : : jl_safe_printf("%p was written a reference to %p that was not recorded\n", lostval_parent, lostval);
240 : : jl_safe_printf("(details above)\n");
241 : : lostval = NULL;
242 : : }
243 : : restore();
244 : : } while(lostval != NULL);
245 : : }
246 : :
247 : : void gc_verify(jl_ptls_t ptls)
248 : : {
249 : : jl_gc_mark_cache_t *gc_cache = &ptls->gc_cache;
250 : : jl_gc_mark_sp_t sp;
251 : : gc_mark_sp_init(gc_cache, &sp);
252 : : lostval = NULL;
253 : : lostval_parents.len = 0;
254 : : lostval_parents_done.len = 0;
255 : : clear_mark(GC_CLEAN);
256 : : gc_verifying = 1;
257 : : gc_mark_queue_all_roots(ptls, &sp);
258 : : gc_mark_queue_finlist(gc_cache, &sp, &to_finalize, 0);
259 : : for (int i = 0;i < jl_n_threads;i++) {
260 : : jl_ptls_t ptls2 = jl_all_tls_states[i];
261 : : gc_mark_queue_finlist(gc_cache, &sp, &ptls2->finalizers, 0);
262 : : }
263 : : gc_mark_queue_finlist(gc_cache, &sp, &finalizer_list_marked, 0);
264 : : gc_mark_loop(ptls, sp);
265 : : int clean_len = bits_save[GC_CLEAN].len;
266 : : for(int i = 0; i < clean_len + bits_save[GC_OLD].len; i++) {
267 : : jl_taggedvalue_t *v = (jl_taggedvalue_t*)bits_save[i >= clean_len ? GC_OLD : GC_CLEAN].items[i >= clean_len ? i - clean_len : i];
268 : : if (gc_marked(v->bits.gc)) {
269 : : jl_safe_printf("Error. Early free of %p type :", v);
270 : : jl_(jl_typeof(jl_valueof(v)));
271 : : jl_safe_printf("val : ");
272 : : jl_(jl_valueof(v));
273 : : jl_safe_printf("Let's try to backtrack the missing write barrier :\n");
274 : : lostval = jl_valueof(v);
275 : : break;
276 : : }
277 : : }
278 : : if (lostval == NULL) {
279 : : gc_verifying = 0;
280 : : restore(); // we did not miss anything
281 : : return;
282 : : }
283 : : restore();
284 : : gc_verify_track(ptls);
285 : : jl_gc_debug_print_status();
286 : : jl_gc_debug_critical_error();
287 : : abort();
288 : : }
289 : : #endif
290 : :
291 : : #ifdef MEMFENCE
292 : : static uint8_t freelist_map[GC_PAGE_SZ / sizeof(void*) / 8];
293 : : static int freelist_zerod;
294 : :
295 : : static void gc_verify_tags_page(jl_gc_pagemeta_t *pg)
296 : : {
297 : : // for all pages in use
298 : : int p_n = pg->pool_n;
299 : : int t_n = pg->thread_n;
300 : : jl_ptls_t ptls2 = jl_all_tls_states[t_n];
301 : : jl_gc_pool_t *p = &ptls2->heap.norm_pools[p_n];
302 : : int osize = pg->osize;
303 : : char *data = pg->data;
304 : : char *page_begin = data + GC_PAGE_OFFSET;
305 : : jl_taggedvalue_t *v = (jl_taggedvalue_t*)page_begin;
306 : : char *lim = data + GC_PAGE_SZ - osize;
307 : : // reset the freelist map to zero
308 : : if (!freelist_zerod) {
309 : : memset(freelist_map, 0, sizeof(freelist_map));
310 : : freelist_zerod = 1;
311 : : }
312 : : // check for p in new newpages list
313 : : jl_taggedvalue_t *halfpages = p->newpages;
314 : : if (halfpages) {
315 : : char *cur_page = gc_page_data((char*)halfpages - 1);
316 : : if (cur_page == data) {
317 : : lim = (char*)halfpages - 1;
318 : : }
319 : : }
320 : : // compute the freelist_map
321 : : if (pg->nfree) {
322 : : jl_taggedvalue_t *next = NULL;
323 : : if (gc_page_data(p->freelist) == data) {
324 : : // currently allocating on this page
325 : : next = p->freelist;
326 : : assert(page_metadata(next)->osize == osize);
327 : : freelist_zerod = 0;
328 : : }
329 : : else if (pg->fl_begin_offset != (uint16_t)-1) {
330 : : // part of free list exists on this page
331 : : next = page_pfl_beg(pg);
332 : : freelist_zerod = 0;
333 : : }
334 : : assert(halfpages || next);
335 : : while (gc_page_data(next) == data) {
336 : : int obj_idx = (((char*)next) - page_begin) / sizeof(void*);
337 : : freelist_map[obj_idx / 8] |= 1 << (obj_idx % 7);
338 : : next = next->next;
339 : : }
340 : : }
341 : : // validate all of the tags on the page
342 : : while ((char*)v <= lim) {
343 : : int obj_idx = (((char*)v) - page_begin) / sizeof(void*);
344 : : int in_freelist = freelist_map[obj_idx / 8] & (1 << (obj_idx % 7));
345 : : if (!in_freelist) {
346 : : jl_value_t *dt = jl_typeof(jl_valueof(v));
347 : : if (dt != (jl_value_t*)jl_buff_tag &&
348 : : // the following are used by the deserializer to invalidate objects
349 : : v->header != 0x10 && v->header != 0x20 &&
350 : : v->header != 0x30 && v->header != 0x40 &&
351 : : v->header != 0x50 && v->header != 0x60) {
352 : : assert(jl_typeof(dt) == (jl_value_t*)jl_datatype_type);
353 : : }
354 : : }
355 : : v = (jl_taggedvalue_t*)((char*)v + osize);
356 : : }
357 : : }
358 : :
359 : : static void gc_verify_tags_pagetable0(pagetable0_t *pagetable0)
360 : : {
361 : : for (int pg_i = 0; pg_i < REGION0_PG_COUNT / 32; pg_i++) {
362 : : uint32_t line = pagetable0->allocmap[pg_i];
363 : : if (line) {
364 : : for (int j = 0; j < 32; j++) {
365 : : if ((line >> j) & 1) {
366 : : gc_verify_tags_page(pagetable0->meta[pg_i * 32 + j]);
367 : : }
368 : : }
369 : : }
370 : : }
371 : : }
372 : :
373 : : static void gc_verify_tags_pagetable1(pagetable1_t *pagetable1)
374 : : {
375 : : for (int pg_i = 0; pg_i < REGION1_PG_COUNT / 32; pg_i++) {
376 : : uint32_t line = pagetable1->allocmap0[pg_i];
377 : : if (line) {
378 : : for (int j = 0; j < 32; j++) {
379 : : if ((line >> j) & 1) {
380 : : gc_verify_tags_pagetable0(pagetable1->meta0[pg_i * 32 + j]);
381 : : }
382 : : }
383 : : }
384 : : }
385 : : }
386 : :
387 : : static void gc_verify_tags_pagetable(void)
388 : : {
389 : : for (int pg_i = 0; pg_i < (REGION2_PG_COUNT + 31) / 32; pg_i++) {
390 : : uint32_t line = memory_map.allocmap1[pg_i];
391 : : if (line) {
392 : : for (int j = 0; j < 32; j++) {
393 : : if ((line >> j) & 1) {
394 : : gc_verify_tags_pagetable1(memory_map.meta1[pg_i * 32 + j]);
395 : : }
396 : : }
397 : : }
398 : : }
399 : : }
400 : :
401 : : void gc_verify_tags(void)
402 : : {
403 : : // verify the freelist chains look valid
404 : : for (int t_i = 0; t_i < jl_n_threads; t_i++) {
405 : : jl_ptls_t ptls2 = jl_all_tls_states[t_i];
406 : : for (int i = 0; i < JL_GC_N_POOLS; i++) {
407 : : // for all pools, iterate its freelist
408 : : jl_gc_pool_t *p = &ptls2->heap.norm_pools[i];
409 : : jl_taggedvalue_t *next = p->freelist;
410 : : jl_taggedvalue_t *last = NULL;
411 : : char *allocating = gc_page_data(next);
412 : : while (next) {
413 : : // and assert that the freelist values aren't gc-marked
414 : : assert(next->bits.gc == 0);
415 : : // TODO: verify they are ordered and on the right byte boundaries
416 : : if (gc_page_data(next) != gc_page_data(last)) {
417 : : // and verify that the chain looks valid
418 : : jl_gc_pagemeta_t *pg = page_metadata(next);
419 : : assert(pg->osize == p->osize);
420 : : if (gc_page_data(next) != allocating) {
421 : : // when not currently allocating on this page, fl_begin_offset should be correct
422 : : assert(next == page_pfl_beg(pg));
423 : : }
424 : : }
425 : : last = next;
426 : : next = next->next;
427 : : }
428 : : }
429 : : }
430 : :
431 : : // verify that all the objects on every page are either valid julia objects
432 : : // or are part of the freelist or are on the allocated half of a page
433 : : gc_verify_tags_pagetable();
434 : : }
435 : : #endif
436 : :
437 : : #ifdef GC_DEBUG_ENV
438 : : JL_DLLEXPORT jl_gc_debug_env_t jl_gc_debug_env = {
439 : : 0, 0,
440 : : {0, UINT64_MAX, 0, 0, 0, {0, 0, 0}},
441 : : {0, UINT64_MAX, 0, 0, 0, {0, 0, 0}},
442 : : {0, UINT64_MAX, 0, 0, 0, {0, 0, 0}}
443 : : };
444 : :
445 : : static void gc_debug_alloc_setnext(jl_alloc_num_t *num)
446 : : {
447 : : uint64_t interv = num->interv;
448 : : if (num->random[0] && num->interv != 1) {
449 : : // Randomly trigger GC with the same average frequency
450 : : double scale = log(1.0 + 1.0 / (double)(num->interv - 1));
451 : : double randinterv = floor(fabs(log(erand48(num->random))) / scale) + 1;
452 : : interv = randinterv >= UINT64_MAX ? UINT64_MAX : (uint64_t)randinterv;
453 : : }
454 : : uint64_t next = num->num + interv;
455 : : if (!num->interv || next > num->max || interv > next)
456 : : next = UINT64_MAX;
457 : : num->next = next;
458 : : }
459 : :
460 : : static void gc_debug_alloc_init(jl_alloc_num_t *num, const char *name)
461 : : {
462 : : static const char *fmt = "JULIA_GC_ALLOC_%s";
463 : : char *buff = (char*)alloca(strlen(fmt) + strlen(name) + 1);
464 : : sprintf(buff, fmt, name);
465 : : char *env = getenv(buff);
466 : : if (!env || !*env)
467 : : return;
468 : : if (*env == 'r') {
469 : : env++;
470 : : for (int i = 0;i < 3;i++) {
471 : : while (num->random[i] == 0) {
472 : : num->random[i] = jl_rand();
473 : : }
474 : : }
475 : : }
476 : : num->interv = 1;
477 : : num->max = UINT64_MAX;
478 : : sscanf(env, "%" SCNd64 ":%" SCNd64 ":%" SCNd64,
479 : : (int64_t*)&num->min, (int64_t*)&num->interv, (int64_t*)&num->max);
480 : : if (num->interv == 0)
481 : : num->interv = 1;
482 : : num->next = num->min;
483 : : }
484 : :
485 : : static int gc_debug_alloc_check(jl_alloc_num_t *num)
486 : : {
487 : : if (++num->num < num->next)
488 : : return 0;
489 : : gc_debug_alloc_setnext(num);
490 : : return 1;
491 : : }
492 : :
493 : : int gc_debug_check_pool(void)
494 : : {
495 : : return gc_debug_alloc_check(&jl_gc_debug_env.pool);
496 : : }
497 : :
498 : : int jl_gc_debug_check_other(void)
499 : : {
500 : : return gc_debug_alloc_check(&jl_gc_debug_env.other);
501 : : }
502 : :
503 : : void jl_gc_debug_print_status(void)
504 : : {
505 : : uint64_t pool_count = jl_gc_debug_env.pool.num;
506 : : uint64_t other_count = jl_gc_debug_env.other.num;
507 : : jl_safe_printf("Allocations: %" PRIu64 " "
508 : : "(Pool: %" PRIu64 "; Other: %" PRIu64 "); GC: %d\n",
509 : : pool_count + other_count, pool_count, other_count, gc_num.pause);
510 : : }
511 : :
512 : : void jl_gc_debug_critical_error(void)
513 : : {
514 : : jl_gc_debug_print_status();
515 : : if (!jl_gc_debug_env.wait_for_debugger)
516 : : return;
517 : : jl_safe_printf("Waiting for debugger to attach\n");
518 : : while (1) {
519 : : sleep(1000);
520 : : }
521 : : }
522 : :
523 : : void jl_gc_debug_print(void)
524 : : {
525 : : if (!gc_debug_alloc_check(&jl_gc_debug_env.print))
526 : : return;
527 : : jl_gc_debug_print_status();
528 : : }
529 : :
530 : : // a list of tasks for conservative stack scan during gc_scrub
531 : : static arraylist_t jl_gc_debug_tasks;
532 : :
533 : : void gc_scrub_record_task(jl_task_t *t)
534 : : {
535 : : arraylist_push(&jl_gc_debug_tasks, t);
536 : : }
537 : :
538 : : static void gc_scrub_range(char *low, char *high)
539 : : {
540 : : jl_ptls_t ptls = jl_current_task->ptls;
541 : : jl_jmp_buf *old_buf = jl_get_safe_restore();
542 : : jl_jmp_buf buf;
543 : : if (jl_setjmp(buf, 0)) {
544 : : jl_set_safe_restore(old_buf);
545 : : return;
546 : : }
547 : : jl_set_safe_restore(&buf);
548 : : low = (char*)((uintptr_t)low & ~(uintptr_t)15);
549 : : for (char **stack_p = ((char**)high) - 1; stack_p > (char**)low; stack_p--) {
550 : : char *p = *stack_p;
551 : : size_t osize;
552 : : jl_taggedvalue_t *tag = jl_gc_find_taggedvalue_pool(p, &osize);
553 : : if (osize <= sizeof(jl_taggedvalue_t) || !tag || gc_marked(tag->bits.gc))
554 : : continue;
555 : : jl_gc_pagemeta_t *pg = page_metadata(tag);
556 : : // Make sure the sweep rebuild the freelist
557 : : pg->has_marked = 1;
558 : : pg->has_young = 1;
559 : : // Find the age bit
560 : : char *page_begin = gc_page_data(tag) + GC_PAGE_OFFSET;
561 : : int obj_id = (((char*)tag) - page_begin) / osize;
562 : : uint8_t *ages = pg->ages + obj_id / 8;
563 : : // Force this to be a young object to save some memory
564 : : // (especially on 32bit where it's more likely to have pointer-like
565 : : // bit patterns)
566 : : *ages &= ~(1 << (obj_id % 8));
567 : : memset(tag, 0xff, osize);
568 : : // set mark to GC_MARKED (young and marked)
569 : : tag->bits.gc = GC_MARKED;
570 : : }
571 : : jl_set_safe_restore(old_buf);
572 : : }
573 : :
574 : : static void gc_scrub_task(jl_task_t *ta)
575 : : {
576 : : int16_t tid = ta->tid;
577 : : jl_ptls_t ptls = jl_current_task->ptls;
578 : : jl_ptls_t ptls2 = NULL;
579 : : if (tid != -1)
580 : : ptls2 = jl_all_tls_states[tid];
581 : :
582 : : char *low;
583 : : char *high;
584 : : if (ta->copy_stack && ptls2 && ta == jl_atomic_load_relaxed(&ptls2->current_task)) {
585 : : low = (char*)ptls2->stackbase - ptls2->stacksize;
586 : : high = (char*)ptls2->stackbase;
587 : : }
588 : : else if (ta->stkbuf) {
589 : : low = (char*)ta->stkbuf;
590 : : high = (char*)ta->stkbuf + ta->bufsz;
591 : : }
592 : : else
593 : : return;
594 : :
595 : : if (ptls == ptls2 && ptls2 && ta == jl_atomic_load_relaxed(&ptls2->current_task)) {
596 : : // scan up to current `sp` for current thread and task
597 : : low = (char*)jl_get_frame_addr();
598 : : }
599 : : gc_scrub_range(low, high);
600 : : }
601 : :
602 : : void gc_scrub(void)
603 : : {
604 : : for (size_t i = 0; i < jl_gc_debug_tasks.len; i++)
605 : : gc_scrub_task((jl_task_t*)jl_gc_debug_tasks.items[i]);
606 : : jl_gc_debug_tasks.len = 0;
607 : : }
608 : : #else
609 : 1 : void jl_gc_debug_critical_error(void)
610 : : {
611 : 1 : }
612 : :
613 : 1 : void jl_gc_debug_print_status(void)
614 : : {
615 : : // May not be accurate but should be helpful enough
616 : 1 : uint64_t pool_count = gc_num.poolalloc;
617 : 1 : uint64_t big_count = gc_num.bigalloc;
618 : 1 : jl_safe_printf("Allocations: %" PRIu64 " "
619 : : "(Pool: %" PRIu64 "; Big: %" PRIu64 "); GC: %d\n",
620 : : pool_count + big_count, pool_count, big_count, gc_num.pause);
621 : 1 : }
622 : : #endif
623 : :
624 : : #ifdef OBJPROFILE
625 : : static htable_t obj_counts[3];
626 : : static htable_t obj_sizes[3];
627 : : void objprofile_count(void *ty, int old, int sz)
628 : : {
629 : : if (gc_verifying) return;
630 : : if ((intptr_t)ty <= 0x10) {
631 : : ty = (void*)jl_buff_tag;
632 : : }
633 : : else if (ty != (void*)jl_buff_tag && ty != jl_malloc_tag &&
634 : : jl_typeof(ty) == (jl_value_t*)jl_datatype_type &&
635 : : ((jl_datatype_t*)ty)->instance) {
636 : : ty = jl_singleton_tag;
637 : : }
638 : : void **bp = ptrhash_bp(&obj_counts[old], ty);
639 : : if (*bp == HT_NOTFOUND)
640 : : *bp = (void*)2;
641 : : else
642 : : (*((intptr_t*)bp))++;
643 : : bp = ptrhash_bp(&obj_sizes[old], ty);
644 : : if (*bp == HT_NOTFOUND)
645 : : *bp = (void*)(intptr_t)(1 + sz);
646 : : else
647 : : *((intptr_t*)bp) += sz;
648 : : }
649 : :
650 : : void objprofile_reset(void)
651 : : {
652 : : for (int g = 0; g < 3; g++) {
653 : : htable_reset(&obj_counts[g], 0);
654 : : htable_reset(&obj_sizes[g], 0);
655 : : }
656 : : }
657 : :
658 : : static void objprofile_print(htable_t nums, htable_t sizes)
659 : : {
660 : : for(int i=0; i < nums.size; i+=2) {
661 : : if (nums.table[i+1] != HT_NOTFOUND) {
662 : : void *ty = nums.table[i];
663 : : int num = (intptr_t)nums.table[i + 1] - 1;
664 : : size_t sz = (uintptr_t)ptrhash_get(&sizes, ty) - 1;
665 : : static const int ptr_hex_width = 2 * sizeof(void*);
666 : : if (sz > 2e9) {
667 : : jl_safe_printf(" %6d : %*.1f GB of (%*p) ",
668 : : num, 6, ((double)sz) / 1024 / 1024 / 1024,
669 : : ptr_hex_width, ty);
670 : : }
671 : : else if (sz > 2e6) {
672 : : jl_safe_printf(" %6d : %*.1f MB of (%*p) ",
673 : : num, 6, ((double)sz) / 1024 / 1024,
674 : : ptr_hex_width, ty);
675 : : }
676 : : else if (sz > 2e3) {
677 : : jl_safe_printf(" %6d : %*.1f kB of (%*p) ",
678 : : num, 6, ((double)sz) / 1024,
679 : : ptr_hex_width, ty);
680 : : }
681 : : else {
682 : : jl_safe_printf(" %6d : %*d B of (%*p) ",
683 : : num, 6, (int)sz, ptr_hex_width, ty);
684 : : }
685 : : if (ty == (void*)jl_buff_tag)
686 : : jl_safe_printf("#<buffer>");
687 : : else if (ty == jl_malloc_tag)
688 : : jl_safe_printf("#<malloc>");
689 : : else if (ty == jl_singleton_tag)
690 : : jl_safe_printf("#<singletons>");
691 : : else
692 : : jl_static_show(JL_STDERR, (jl_value_t*)ty);
693 : : jl_safe_printf("\n");
694 : : }
695 : : }
696 : : }
697 : :
698 : : void objprofile_printall(void)
699 : : {
700 : : jl_safe_printf("Transient mark :\n");
701 : : objprofile_print(obj_counts[0], obj_sizes[0]);
702 : : jl_safe_printf("Perm mark :\n");
703 : : objprofile_print(obj_counts[1], obj_sizes[1]);
704 : : jl_safe_printf("Remset :\n");
705 : : objprofile_print(obj_counts[2], obj_sizes[2]);
706 : : }
707 : : #endif
708 : :
709 : : #if defined(GC_TIME) || defined(GC_FINAL_STATS)
710 : : STATIC_INLINE double jl_ns2ms(int64_t t)
711 : : {
712 : : return t / (double)1e6;
713 : : }
714 : :
715 : : STATIC_INLINE double jl_ns2s(int64_t t)
716 : : {
717 : : return t / (double)1e9;
718 : : }
719 : :
720 : : static uint64_t gc_premark_end;
721 : : static uint64_t gc_postmark_end;
722 : : void gc_settime_premark_end(void)
723 : : {
724 : : gc_premark_end = jl_hrtime();
725 : : }
726 : : void gc_settime_postmark_end(void)
727 : : {
728 : : gc_postmark_end = jl_hrtime();
729 : : }
730 : : #endif
731 : :
732 : : #ifdef GC_FINAL_STATS
733 : : #ifdef _OS_LINUX_
734 : : #include <malloc.h> // for mallinfo
735 : : #endif
736 : : static double process_t0;
737 : : static size_t max_pg_count = 0;
738 : : static size_t total_freed_bytes = 0;
739 : : static uint64_t max_pause = 0;
740 : : static uint64_t total_sweep_time = 0;
741 : : static uint64_t total_mark_time = 0;
742 : : static uint64_t total_fin_time = 0;
743 : :
744 : : void gc_final_count_page(size_t pg_cnt)
745 : : {
746 : : if (pg_cnt > max_pg_count) {
747 : : max_pg_count = pg_cnt;
748 : : }
749 : : }
750 : :
751 : : void gc_final_pause_end(int64_t t0, int64_t tend)
752 : : {
753 : : uint64_t post_time = gc_postmark_end - gc_premark_end;
754 : : uint64_t sweep_pause = tend - gc_premark_end;
755 : : uint64_t pause = tend - t0;
756 : : total_freed_bytes += gc_num.freed;
757 : : total_sweep_time += sweep_pause - post_time;
758 : : total_fin_time += post_time;
759 : : max_pause = max_pause < pause ? pause : max_pause;
760 : : total_mark_time += gc_premark_end - t0;
761 : : }
762 : :
763 : : static void gc_stats_pagetable0(pagetable0_t *pagetable0, unsigned *p0)
764 : : {
765 : : for (int pg_i = 0; pg_i < REGION0_PG_COUNT / 32; pg_i++) {
766 : : uint32_t line = pagetable0->allocmap[pg_i] | pagetable0->freemap[pg_i];
767 : : if (line) {
768 : : for (int j = 0; j < 32; j++) {
769 : : if ((line >> j) & 1) {
770 : : (*p0)++;
771 : : }
772 : : }
773 : : }
774 : : }
775 : : }
776 : :
777 : : static void gc_stats_pagetable1(pagetable1_t *pagetable1, unsigned *p1, unsigned *p0)
778 : : {
779 : : for (int pg_i = 0; pg_i < REGION1_PG_COUNT / 32; pg_i++) {
780 : : uint32_t line = pagetable1->allocmap0[pg_i] | pagetable1->freemap0[pg_i];
781 : : if (line) {
782 : : for (int j = 0; j < 32; j++) {
783 : : if ((line >> j) & 1) {
784 : : (*p1)++;
785 : : gc_stats_pagetable0(pagetable1->meta0[pg_i * 32 + j], p0);
786 : : }
787 : : }
788 : : }
789 : : }
790 : : }
791 : :
792 : : static void gc_stats_pagetable(unsigned *p2, unsigned *p1, unsigned *p0)
793 : : {
794 : : for (int pg_i = 0; pg_i < (REGION2_PG_COUNT + 31) / 32; pg_i++) {
795 : : uint32_t line = memory_map.allocmap1[pg_i] | memory_map.freemap1[pg_i];
796 : : if (line) {
797 : : for (int j = 0; j < 32; j++) {
798 : : if ((line >> j) & 1) {
799 : : (*p2)++;
800 : : gc_stats_pagetable1(memory_map.meta1[pg_i * 32 + j], p1, p0);
801 : : }
802 : : }
803 : : }
804 : : }
805 : : }
806 : :
807 : : void jl_print_gc_stats(JL_STREAM *s)
808 : : {
809 : : #ifdef _OS_LINUX_
810 : : malloc_stats();
811 : : #endif
812 : : double ptime = jl_clock_now() - process_t0;
813 : : jl_safe_printf("exec time\t%.5f sec\n", ptime);
814 : : if (gc_num.pause > 0) {
815 : : jl_safe_printf("gc time \t%.5f sec (%2.1f%%) in %d (%d full) collections\n",
816 : : jl_ns2s(gc_num.total_time),
817 : : jl_ns2s(gc_num.total_time) / ptime * 100,
818 : : gc_num.pause, gc_num.full_sweep);
819 : : jl_safe_printf("gc pause \t%.2f ms avg\n\t\t%2.0f ms max\n",
820 : : jl_ns2ms(gc_num.total_time) / gc_num.pause,
821 : : jl_ns2ms(max_pause));
822 : : jl_safe_printf("\t\t(%2d%% mark, %2d%% sweep, %2d%% finalizers)\n",
823 : : (int)(total_mark_time * 100 / gc_num.total_time),
824 : : (int)(total_sweep_time * 100 / gc_num.total_time),
825 : : (int)(total_fin_time * 100 / gc_num.total_time));
826 : : }
827 : : unsigned p2 = 0, p1 = 0, p0 = 0;
828 : : gc_stats_pagetable(&p2, &p1, &p0);
829 : : jl_safe_printf("page table max utilization : %u (%.1f%%) - %u (%.1f%%) - %u (%.1f%%)\n",
830 : : p2, p2 * 100.0 / REGION2_PG_COUNT,
831 : : p1, p1 * 100.0 / REGION1_PG_COUNT / p2,
832 : : p0, p0 * 100.0 / REGION0_PG_COUNT / p1);
833 : : #ifdef _OS_LINUX_
834 : : double gct = gc_num.total_time / 1e9;
835 : : struct mallinfo mi = mallinfo();
836 : : jl_safe_printf("malloc size\t%d MB\n", mi.uordblks / 1024 / 1024);
837 : : jl_safe_printf("max page alloc\t%ld MB\n", max_pg_count * GC_PAGE_SZ / 1024 / 1024);
838 : : jl_safe_printf("total freed\t%" PRIuPTR " b\n", total_freed_bytes);
839 : : jl_safe_printf("free rate\t%.1f MB/sec\n", (total_freed_bytes / gct) / 1024 / 1024);
840 : : #endif
841 : : }
842 : : #else
843 : 639 : void jl_print_gc_stats(JL_STREAM *s)
844 : : {
845 : 639 : }
846 : : #endif
847 : :
848 : : #ifdef GC_TIME
849 : : static int64_t skipped_pages = 0;
850 : : static int64_t total_pages = 0;
851 : : static int64_t freed_pages = 0;
852 : : static int64_t pool_sweep_start;
853 : :
854 : : void gc_time_pool_start(void)
855 : : {
856 : : skipped_pages = 0;
857 : : total_pages = 0;
858 : : freed_pages = 0;
859 : : pool_sweep_start = jl_hrtime();
860 : : }
861 : :
862 : : void gc_time_count_page(int freedall, int pg_skpd)
863 : : {
864 : : freed_pages += freedall;
865 : : skipped_pages += pg_skpd;
866 : : total_pages++;
867 : : }
868 : :
869 : : void gc_time_pool_end(int sweep_full)
870 : : {
871 : : double sweep_pool_sec = (jl_hrtime() - pool_sweep_start) / 1e9;
872 : : double sweep_gb = total_pages * GC_PAGE_SZ / (double)(1024 * 1024 * 1024);
873 : : double sweep_speed = sweep_gb / sweep_pool_sec;
874 : : jl_safe_printf("GC sweep pools end %.2f ms at %.1f GB/s "
875 : : "(skipped %.2f %% of %" PRId64 ", swept %" PRId64 " pgs, "
876 : : "%" PRId64 " freed with %" PRId64 " lazily) %s\n",
877 : : sweep_pool_sec * 1000, sweep_speed,
878 : : (total_pages ? ((double)skipped_pages * 100) / total_pages : 0),
879 : : total_pages, total_pages - skipped_pages,
880 : : freed_pages, lazy_freed_pages,
881 : : sweep_full ? "full" : "quick");
882 : : }
883 : :
884 : : void gc_time_sysimg_end(uint64_t t0)
885 : : {
886 : : double sweep_pool_sec = (jl_hrtime() - t0) / 1e9;
887 : : jl_safe_printf("GC sweep sysimg end %.2f ms\n",
888 : : sweep_pool_sec * 1000);
889 : : }
890 : :
891 : : static int64_t big_total;
892 : : static int64_t big_freed;
893 : : static int64_t big_reset;
894 : : static int64_t big_sweep_start;
895 : :
896 : : void gc_time_big_start(void)
897 : : {
898 : : big_total = 0;
899 : : big_freed = 0;
900 : : big_reset = 0;
901 : : big_sweep_start = jl_hrtime();
902 : : }
903 : :
904 : : void gc_time_count_big(int old_bits, int bits)
905 : : {
906 : : big_total++;
907 : : big_reset += bits == GC_CLEAN;
908 : : big_freed += !gc_marked(old_bits);
909 : : }
910 : :
911 : : void gc_time_big_end(void)
912 : : {
913 : : double t_ms = jl_ns2ms(jl_hrtime() - big_sweep_start);
914 : : jl_safe_printf("GC sweep big %.2f ms "
915 : : "(freed %" PRId64 " / %" PRId64 " with %" PRId64 " rst)\n",
916 : : t_ms, big_freed, big_total, big_reset);
917 : : }
918 : :
919 : : static int64_t mallocd_array_total;
920 : : static int64_t mallocd_array_freed;
921 : : static int64_t mallocd_array_sweep_start;
922 : :
923 : : void gc_time_mallocd_array_start(void)
924 : : {
925 : : mallocd_array_total = 0;
926 : : mallocd_array_freed = 0;
927 : : mallocd_array_sweep_start = jl_hrtime();
928 : : }
929 : :
930 : : void gc_time_count_mallocd_array(int bits)
931 : : {
932 : : mallocd_array_total++;
933 : : mallocd_array_freed += !gc_marked(bits);
934 : : }
935 : :
936 : : void gc_time_mallocd_array_end(void)
937 : : {
938 : : double t_ms = jl_ns2ms(jl_hrtime() - mallocd_array_sweep_start);
939 : : jl_safe_printf("GC sweep arrays %.2f ms "
940 : : "(freed %" PRId64 " / %" PRId64 ")\n",
941 : : t_ms, mallocd_array_freed, mallocd_array_total);
942 : : }
943 : :
944 : : void gc_time_mark_pause(int64_t t0, int64_t scanned_bytes,
945 : : int64_t perm_scanned_bytes)
946 : : {
947 : : int64_t last_remset_len = 0;
948 : : int64_t remset_nptr = 0;
949 : : for (int t_i = 0;t_i < jl_n_threads;t_i++) {
950 : : jl_ptls_t ptls2 = jl_all_tls_states[t_i];
951 : : last_remset_len += ptls2->heap.last_remset->len;
952 : : remset_nptr = ptls2->heap.remset_nptr;
953 : : }
954 : : jl_safe_printf("GC mark pause %.2f ms | "
955 : : "scanned %" PRId64 " kB = %" PRId64 " + %" PRId64 " | "
956 : : "remset %" PRId64 " %" PRId64 "\n",
957 : : jl_ns2ms(gc_premark_end - t0),
958 : : (scanned_bytes + perm_scanned_bytes) / 1024,
959 : : scanned_bytes / 1024, perm_scanned_bytes / 1024,
960 : : last_remset_len, remset_nptr);
961 : : }
962 : :
963 : : void gc_time_sweep_pause(uint64_t gc_end_t, int64_t actual_allocd,
964 : : int64_t live_bytes, int64_t estimate_freed,
965 : : int sweep_full)
966 : : {
967 : : uint64_t sweep_pause = gc_end_t - gc_premark_end;
968 : : int pct = actual_allocd ? (gc_num.freed * 100) / actual_allocd : -1;
969 : : jl_safe_printf("GC sweep pause %.2f ms live %" PRId64 " kB "
970 : : "(freed %" PRId64 " kB EST %" PRId64 " kB "
971 : : "[error %" PRId64 "] = %d%% of allocd b %" PRIu64 ") "
972 : : "(%.2f ms in post_mark) %s | next in %" PRId64 " kB\n",
973 : : jl_ns2ms(sweep_pause), live_bytes / 1024,
974 : : gc_num.freed / 1024, estimate_freed / 1024,
975 : : gc_num.freed - estimate_freed, pct, gc_num.since_sweep / 1024,
976 : : jl_ns2ms(gc_postmark_end - gc_premark_end),
977 : : sweep_full ? "full" : "quick", -gc_num.allocd / 1024);
978 : : }
979 : :
980 : : void gc_time_summary(int sweep_full, uint64_t start, uint64_t end,
981 : : uint64_t freed, uint64_t live, uint64_t interval,
982 : : uint64_t pause, uint64_t ttsp, uint64_t mark,
983 : : uint64_t sweep)
984 : : {
985 : : if (sweep_full > 0)
986 : : jl_safe_printf("TS: %" PRIu64 " Major collection: estimate freed = %" PRIu64
987 : : " live = %" PRIu64 "m new interval = %" PRIu64
988 : : "m time = %" PRIu64 "ms ttsp = %" PRIu64 "us mark time = %"
989 : : PRIu64 "ms sweep time = %" PRIu64 "ms \n",
990 : : end, freed, live/1024/1024,
991 : : interval/1024/1024, pause/1000000, ttsp,
992 : : mark/1000000,sweep/1000000);
993 : : else
994 : : jl_safe_printf("TS: %" PRIu64 " Minor collection: estimate freed = %" PRIu64
995 : : " live = %" PRIu64 "m new interval = %" PRIu64 "m pause time = %"
996 : : PRIu64 "ms ttsp = %" PRIu64 "us mark time = %" PRIu64
997 : : "ms sweep time = %" PRIu64 "ms \n",
998 : : end, freed, live/1024/1024,
999 : : interval/1024/1024, pause/1000000, ttsp,
1000 : : mark/1000000,sweep/1000000);
1001 : : }
1002 : : #endif
1003 : :
1004 : 573 : void jl_gc_debug_init(void)
1005 : : {
1006 : : #ifdef GC_DEBUG_ENV
1007 : : char *env = getenv("JULIA_GC_NO_GENERATIONAL");
1008 : : if (env && strcmp(env, "0") != 0)
1009 : : jl_gc_debug_env.always_full = 1;
1010 : : env = getenv("JULIA_GC_WAIT_FOR_DEBUGGER");
1011 : : jl_gc_debug_env.wait_for_debugger = env && strcmp(env, "0") != 0;
1012 : : gc_debug_alloc_init(&jl_gc_debug_env.pool, "POOL");
1013 : : gc_debug_alloc_init(&jl_gc_debug_env.other, "OTHER");
1014 : : gc_debug_alloc_init(&jl_gc_debug_env.print, "PRINT");
1015 : : arraylist_new(&jl_gc_debug_tasks, 0);
1016 : : #endif
1017 : :
1018 : : #ifdef GC_VERIFY
1019 : : for (int i = 0; i < 4; i++)
1020 : : arraylist_new(&bits_save[i], 0);
1021 : : arraylist_new(&lostval_parents, 0);
1022 : : arraylist_new(&lostval_parents_done, 0);
1023 : : #endif
1024 : :
1025 : : #ifdef OBJPROFILE
1026 : : for (int g = 0;g < 3;g++) {
1027 : : htable_new(&obj_counts[g], 0);
1028 : : htable_new(&obj_sizes[g], 0);
1029 : : }
1030 : : #endif
1031 : :
1032 : : #ifdef GC_FINAL_STATS
1033 : : process_t0 = jl_clock_now();
1034 : : #endif
1035 : 573 : }
1036 : :
1037 : : // GC summary stats
1038 : :
1039 : : #ifdef MEMPROFILE
1040 : : // TODO repair this and possibly merge with `gc_count_pool`
1041 : : static size_t pool_stats(jl_gc_pool_t *p, size_t *pwaste, size_t *np,
1042 : : size_t *pnold)
1043 : : {
1044 : : jl_taggedvalue_t *halfpages = p->newpages;
1045 : : size_t osize = p->osize;
1046 : : size_t nused=0, nfree=0, npgs=0, nold=0;
1047 : :
1048 : : if (halfpages != NULL) {
1049 : : npgs++;
1050 : : char *v = gc_page_data(halfpages) + GC_PAGE_OFFSET;
1051 : : char *lim = (char*)halfpages - 1;
1052 : : int i = 0;
1053 : : while (v <= lim) {
1054 : : if (!gc_marked(((jl_taggedvalue_t*)v)->bits.gc)) {
1055 : : nfree++;
1056 : : }
1057 : : else {
1058 : : nused++;
1059 : : if (((jl_taggedvalue_t*)v)->bits.gc == GC_OLD_MARKED) {
1060 : : nold++;
1061 : : }
1062 : : }
1063 : : v = v + osize;
1064 : : i++;
1065 : : }
1066 : : // only the first page is allocated on
1067 : : }
1068 : : *pwaste = npgs * GC_PAGE_SZ - (nused * p->osize);
1069 : : *np = npgs;
1070 : : *pnold = nold;
1071 : : if (npgs != 0) {
1072 : : jl_safe_printf("%4d : %7lld/%7lld objects (%3lld%% old), %5lld pages, %5lld kB, %5lld kB waste\n",
1073 : : p->osize,
1074 : : (long long)nused,
1075 : : (long long)(nused + nfree),
1076 : : (long long)(nused ? (nold * 100) / nused : 0),
1077 : : (long long)npgs,
1078 : : (long long)((nused * p->osize) / 1024),
1079 : : (long long)(*pwaste / 1024));
1080 : : }
1081 : : return nused*p->osize;
1082 : : }
1083 : :
1084 : : void gc_stats_all_pool(void)
1085 : : {
1086 : : size_t nb=0, w, tw=0, no=0, tp=0, nold=0, noldbytes=0, np, nol;
1087 : : for (int i = 0; i < JL_GC_N_POOLS; i++) {
1088 : : for (int t_i = 0; t_i < jl_n_threads; t_i++) {
1089 : : jl_ptls_t ptls2 = jl_all_tls_states[t_i];
1090 : : size_t b = pool_stats(&ptls2->heap.norm_pools[i], &w, &np, &nol);
1091 : : nb += b;
1092 : : no += (b / ptls2->heap.norm_pools[i].osize);
1093 : : tw += w;
1094 : : tp += np;
1095 : : nold += nol;
1096 : : noldbytes += nol * ptls2->heap.norm_pools[i].osize;
1097 : : }
1098 : : }
1099 : : jl_safe_printf("%lld objects (%lld%% old), %lld kB (%lld%% old) total allocated, "
1100 : : "%lld total fragments (%lld%% overhead), in %lld pages\n",
1101 : : (long long)no,
1102 : : (long long)(no ? (nold * 100) / no : 0),
1103 : : (long long)(nb / 1024),
1104 : : (long long)(nb ? (noldbytes * 100) / nb : 0),
1105 : : (long long)tw,
1106 : : (long long)(nb ? (tw * 100) / nb : 0),
1107 : : (long long)tp);
1108 : : }
1109 : :
1110 : : void gc_stats_big_obj(void)
1111 : : {
1112 : : size_t nused=0, nbytes=0, nused_old=0, nbytes_old=0;
1113 : : for (int t_i = 0; t_i < jl_n_threads; t_i++) {
1114 : : jl_ptls_t ptls2 = jl_all_tls_states[t_i];
1115 : : bigval_t *v = ptls2->heap.big_objects;
1116 : : while (v != NULL) {
1117 : : if (gc_marked(v->bits.gc)) {
1118 : : nused++;
1119 : : nbytes += v->sz & ~3;
1120 : : }
1121 : : v = v->next;
1122 : : }
1123 : : v = big_objects_marked;
1124 : : while (v != NULL) {
1125 : : if (gc_marked(v->bits.gc)) {
1126 : : nused_old++;
1127 : : nbytes_old += v->sz & ~3;
1128 : : }
1129 : : v = v->next;
1130 : : }
1131 : :
1132 : : mallocarray_t *ma = ptls2->heap.mallocarrays;
1133 : : while (ma != NULL) {
1134 : : if (gc_marked(jl_astaggedvalue(ma->a)->bits.gc)) {
1135 : : nused++;
1136 : : nbytes += jl_array_nbytes(ma->a);
1137 : : }
1138 : : ma = ma->next;
1139 : : }
1140 : : }
1141 : :
1142 : : jl_safe_printf("%lld kB (%lld%% old) in %lld large objects (%lld%% old)\n",
1143 : : (long long)((nbytes + nbytes_old) / 1024),
1144 : : (long long)(nbytes + nbytes_old ? (nbytes_old * 100) / (nbytes + nbytes_old) : 0),
1145 : : (long long)(nused + nused_old),
1146 : : (long long)(nused + nused_old ? (nused_old * 100) / (nused + nused_old) : 0));
1147 : : }
1148 : : #endif //MEMPROFILE
1149 : :
1150 : : // Simple and dumb way to count cells with different gc bits in allocated pages
1151 : : // Use as ground truth for debugging memory-leak-like issues.
1152 : : static int64_t poolobj_sizes[4];
1153 : : static int64_t empty_pages;
1154 : :
1155 : 0 : static void gc_count_pool_page(jl_gc_pagemeta_t *pg)
1156 : : {
1157 : 0 : int osize = pg->osize;
1158 : 0 : char *data = pg->data;
1159 : 0 : jl_taggedvalue_t *v = (jl_taggedvalue_t*)(data + GC_PAGE_OFFSET);
1160 : 0 : char *lim = (char*)v + GC_PAGE_SZ - GC_PAGE_OFFSET - osize;
1161 : 0 : int has_live = 0;
1162 [ # # ]: 0 : while ((char*)v <= lim) {
1163 : 0 : int bits = v->bits.gc;
1164 [ # # ]: 0 : if (gc_marked(bits))
1165 : 0 : has_live = 1;
1166 : 0 : poolobj_sizes[bits] += osize;
1167 : 0 : v = (jl_taggedvalue_t*)((char*)v + osize);
1168 : : }
1169 [ # # ]: 0 : if (!has_live) {
1170 : 0 : empty_pages++;
1171 : : }
1172 : 0 : }
1173 : :
1174 : 0 : static void gc_count_pool_pagetable0(pagetable0_t *pagetable0)
1175 : : {
1176 [ # # ]: 0 : for (int pg_i = 0; pg_i < REGION0_PG_COUNT / 32; pg_i++) {
1177 : 0 : uint32_t line = pagetable0->allocmap[pg_i];
1178 [ # # ]: 0 : if (line) {
1179 [ # # ]: 0 : for (int j = 0; j < 32; j++) {
1180 [ # # ]: 0 : if ((line >> j) & 1) {
1181 : 0 : gc_count_pool_page(pagetable0->meta[pg_i * 32 + j]);
1182 : : }
1183 : : }
1184 : : }
1185 : : }
1186 : 0 : }
1187 : :
1188 : 0 : static void gc_count_pool_pagetable1(pagetable1_t *pagetable1)
1189 : : {
1190 [ # # ]: 0 : for (int pg_i = 0; pg_i < REGION1_PG_COUNT / 32; pg_i++) {
1191 : 0 : uint32_t line = pagetable1->allocmap0[pg_i];
1192 [ # # ]: 0 : if (line) {
1193 [ # # ]: 0 : for (int j = 0; j < 32; j++) {
1194 [ # # ]: 0 : if ((line >> j) & 1) {
1195 : 0 : gc_count_pool_pagetable0(pagetable1->meta0[pg_i * 32 + j]);
1196 : : }
1197 : : }
1198 : : }
1199 : : }
1200 : 0 : }
1201 : :
1202 : 0 : static void gc_count_pool_pagetable(void)
1203 : : {
1204 [ # # ]: 0 : for (int pg_i = 0; pg_i < (REGION2_PG_COUNT + 31) / 32; pg_i++) {
1205 : 0 : uint32_t line = memory_map.allocmap1[pg_i];
1206 [ # # ]: 0 : if (line) {
1207 [ # # ]: 0 : for (int j = 0; j < 32; j++) {
1208 [ # # ]: 0 : if ((line >> j) & 1) {
1209 : 0 : gc_count_pool_pagetable1(memory_map.meta1[pg_i * 32 + j]);
1210 : : }
1211 : : }
1212 : : }
1213 : : }
1214 : 0 : }
1215 : :
1216 : 0 : void gc_count_pool(void)
1217 : : {
1218 : 0 : memset(&poolobj_sizes, 0, sizeof(poolobj_sizes));
1219 : 0 : empty_pages = 0;
1220 : 0 : gc_count_pool_pagetable();
1221 : 0 : jl_safe_printf("****** Pool stat: ******\n");
1222 [ # # ]: 0 : for (int i = 0;i < 4;i++)
1223 : 0 : jl_safe_printf("bits(%d): %" PRId64 "\n", i, poolobj_sizes[i]);
1224 : : // empty_pages is inaccurate after the sweep since young objects are
1225 : : // also GC_CLEAN
1226 : 0 : jl_safe_printf("free pages: % " PRId64 "\n", empty_pages);
1227 : 0 : jl_safe_printf("************************\n");
1228 : 0 : }
1229 : :
1230 : 0 : int gc_slot_to_fieldidx(void *obj, void *slot)
1231 : : {
1232 : 0 : jl_datatype_t *vt = (jl_datatype_t*)jl_typeof(obj);
1233 : 0 : int nf = (int)jl_datatype_nfields(vt);
1234 [ # # ]: 0 : for (int i = 0; i < nf; i++) {
1235 : 0 : void *fieldaddr = (char*)obj + jl_field_offset(vt, i);
1236 [ # # ]: 0 : if (fieldaddr >= slot) {
1237 : 0 : return i;
1238 : : }
1239 : : }
1240 : 0 : return -1;
1241 : : }
1242 : :
1243 : 0 : int gc_slot_to_arrayidx(void *obj, void *_slot)
1244 : : {
1245 : 0 : char *slot = (char*)_slot;
1246 : 0 : jl_datatype_t *vt = (jl_datatype_t*)jl_typeof(obj);
1247 : 0 : char *start = NULL;
1248 : 0 : size_t len = 0;
1249 : 0 : size_t elsize = sizeof(void*);
1250 [ # # ]: 0 : if (vt == jl_module_type) {
1251 : 0 : jl_module_t *m = (jl_module_t*)obj;
1252 : 0 : start = (char*)m->usings.items;
1253 : 0 : len = m->usings.len;
1254 : : }
1255 [ # # ]: 0 : else if (vt == jl_simplevector_type) {
1256 : 0 : start = (char*)jl_svec_data(obj);
1257 : 0 : len = jl_svec_len(obj);
1258 : : }
1259 [ # # ]: 0 : else if (vt->name == jl_array_typename) {
1260 : 0 : jl_array_t *a = (jl_array_t*)obj;
1261 [ # # ]: 0 : if (!a->flags.ptrarray)
1262 : 0 : return -1;
1263 : 0 : start = (char*)a->data;
1264 : 0 : len = jl_array_len(a);
1265 : 0 : elsize = a->elsize;
1266 : : }
1267 [ # # # # ]: 0 : if (slot < start || slot >= start + elsize * len)
1268 : 0 : return -1;
1269 : 0 : return (slot - start) / elsize;
1270 : : }
1271 : :
1272 : : // Print a backtrace from the bottom (start) of the mark stack up to `sp`
1273 : : // `pc_offset` will be added to `sp` for convenience in the debugger.
1274 : 0 : NOINLINE void gc_mark_loop_unwind(jl_ptls_t ptls, jl_gc_mark_sp_t sp, int pc_offset)
1275 : : {
1276 : 0 : jl_jmp_buf *old_buf = jl_get_safe_restore();
1277 : : jl_jmp_buf buf;
1278 : 0 : jl_set_safe_restore(&buf);
1279 [ # # ]: 0 : if (jl_setjmp(buf, 0) != 0) {
1280 : 0 : jl_safe_printf("\n!!! ERROR when unwinding gc mark loop -- ABORTING !!!\n");
1281 : 0 : jl_set_safe_restore(old_buf);
1282 : 0 : return;
1283 : : }
1284 : 0 : void **top = sp.pc + pc_offset;
1285 : 0 : jl_gc_mark_data_t *data_top = sp.data;
1286 : 0 : sp.data = ptls->gc_cache.data_stack;
1287 : 0 : sp.pc = ptls->gc_cache.pc_stack;
1288 : 0 : int isroot = 1;
1289 [ # # ]: 0 : while (sp.pc < top) {
1290 : 0 : void *pc = *sp.pc;
1291 [ # # ]: 0 : const char *prefix = isroot ? "r--" : " `-";
1292 : 0 : isroot = 0;
1293 [ # # ]: 0 : if (pc == gc_mark_label_addrs[GC_MARK_L_marked_obj]) {
1294 : 0 : gc_mark_marked_obj_t *data = gc_repush_markdata(&sp, gc_mark_marked_obj_t);
1295 [ # # ]: 0 : if ((jl_gc_mark_data_t *)data > data_top) {
1296 : 0 : jl_safe_printf("Mark stack unwind overflow -- ABORTING !!!\n");
1297 : 0 : break;
1298 : : }
1299 : 0 : jl_safe_printf("%p: Root object: %p :: %p (bits: %d)\n of type ",
1300 : 0 : (void*)data, (void*)data->obj, (void*)data->tag, (int)data->bits);
1301 : 0 : jl_((void*)data->tag);
1302 : 0 : isroot = 1;
1303 : : }
1304 [ # # ]: 0 : else if (pc == gc_mark_label_addrs[GC_MARK_L_scan_only]) {
1305 : 0 : gc_mark_marked_obj_t *data = gc_repush_markdata(&sp, gc_mark_marked_obj_t);
1306 [ # # ]: 0 : if ((jl_gc_mark_data_t *)data > data_top) {
1307 : 0 : jl_safe_printf("Mark stack unwind overflow -- ABORTING !!!\n");
1308 : 0 : break;
1309 : : }
1310 : 0 : jl_safe_printf("%p: Queued root: %p :: %p (bits: %d)\n of type ",
1311 : 0 : (void*)data, (void*)data->obj, (void*)data->tag, (int)data->bits);
1312 : 0 : jl_((void*)data->tag);
1313 : 0 : isroot = 1;
1314 : : }
1315 [ # # ]: 0 : else if (pc == gc_mark_label_addrs[GC_MARK_L_finlist]) {
1316 : 0 : gc_mark_finlist_t *data = gc_repush_markdata(&sp, gc_mark_finlist_t);
1317 [ # # ]: 0 : if ((jl_gc_mark_data_t *)data > data_top) {
1318 : 0 : jl_safe_printf("Mark stack unwind overflow -- ABORTING !!!\n");
1319 : 0 : break;
1320 : : }
1321 : 0 : jl_safe_printf("%p: Finalizer list from %p to %p\n",
1322 : 0 : (void*)data, (void*)data->begin, (void*)data->end);
1323 : 0 : isroot = 1;
1324 : : }
1325 [ # # ]: 0 : else if (pc == gc_mark_label_addrs[GC_MARK_L_objarray]) {
1326 : 0 : gc_mark_objarray_t *data = gc_repush_markdata(&sp, gc_mark_objarray_t);
1327 [ # # ]: 0 : if ((jl_gc_mark_data_t *)data > data_top) {
1328 : 0 : jl_safe_printf("Mark stack unwind overflow -- ABORTING !!!\n");
1329 : 0 : break;
1330 : : }
1331 : 0 : jl_safe_printf("%p: %s Array in object %p :: %p -- [%p, %p)\n of type ",
1332 : 0 : (void*)data, prefix, (void*)data->parent, ((void**)data->parent)[-1],
1333 : 0 : (void*)data->begin, (void*)data->end);
1334 : 0 : jl_(jl_typeof(data->parent));
1335 : : }
1336 [ # # ]: 0 : else if (pc == gc_mark_label_addrs[GC_MARK_L_obj8]) {
1337 : 0 : gc_mark_obj8_t *data = gc_repush_markdata(&sp, gc_mark_obj8_t);
1338 [ # # ]: 0 : if ((jl_gc_mark_data_t *)data > data_top) {
1339 : 0 : jl_safe_printf("Mark stack unwind overflow -- ABORTING !!!\n");
1340 : 0 : break;
1341 : : }
1342 : 0 : jl_datatype_t *vt = (jl_datatype_t*)jl_typeof(data->parent);
1343 : 0 : uint8_t *desc = (uint8_t*)jl_dt_layout_ptrs(vt->layout);
1344 : 0 : jl_safe_printf("%p: %s Object (8bit) %p :: %p -- [%d, %d)\n of type ",
1345 : 0 : (void*)data, prefix, (void*)data->parent, ((void**)data->parent)[-1],
1346 : 0 : (int)(data->begin - desc), (int)(data->end - desc));
1347 : 0 : jl_(jl_typeof(data->parent));
1348 : : }
1349 [ # # ]: 0 : else if (pc == gc_mark_label_addrs[GC_MARK_L_obj16]) {
1350 : 0 : gc_mark_obj16_t *data = gc_repush_markdata(&sp, gc_mark_obj16_t);
1351 [ # # ]: 0 : if ((jl_gc_mark_data_t *)data > data_top) {
1352 : 0 : jl_safe_printf("Mark stack unwind overflow -- ABORTING !!!\n");
1353 : 0 : break;
1354 : : }
1355 : 0 : jl_datatype_t *vt = (jl_datatype_t*)jl_typeof(data->parent);
1356 : 0 : uint16_t *desc = (uint16_t*)jl_dt_layout_ptrs(vt->layout);
1357 : 0 : jl_safe_printf("%p: %s Object (16bit) %p :: %p -- [%d, %d)\n of type ",
1358 : 0 : (void*)data, prefix, (void*)data->parent, ((void**)data->parent)[-1],
1359 : 0 : (int)(data->begin - desc), (int)(data->end - desc));
1360 : 0 : jl_(jl_typeof(data->parent));
1361 : : }
1362 [ # # ]: 0 : else if (pc == gc_mark_label_addrs[GC_MARK_L_obj32]) {
1363 : 0 : gc_mark_obj32_t *data = gc_repush_markdata(&sp, gc_mark_obj32_t);
1364 [ # # ]: 0 : if ((jl_gc_mark_data_t *)data > data_top) {
1365 : 0 : jl_safe_printf("Mark stack unwind overflow -- ABORTING !!!\n");
1366 : 0 : break;
1367 : : }
1368 : 0 : jl_datatype_t *vt = (jl_datatype_t*)jl_typeof(data->parent);
1369 : 0 : uint32_t *desc = (uint32_t*)jl_dt_layout_ptrs(vt->layout);
1370 : 0 : jl_safe_printf("%p: %s Object (32bit) %p :: %p -- [%d, %d)\n of type ",
1371 : 0 : (void*)data, prefix, (void*)data->parent, ((void**)data->parent)[-1],
1372 : 0 : (int)(data->begin - desc), (int)(data->end - desc));
1373 : 0 : jl_(jl_typeof(data->parent));
1374 : : }
1375 [ # # ]: 0 : else if (pc == gc_mark_label_addrs[GC_MARK_L_stack]) {
1376 : 0 : gc_mark_stackframe_t *data = gc_repush_markdata(&sp, gc_mark_stackframe_t);
1377 [ # # ]: 0 : if ((jl_gc_mark_data_t *)data > data_top) {
1378 : 0 : jl_safe_printf("Mark stack unwind overflow -- ABORTING !!!\n");
1379 : 0 : break;
1380 : : }
1381 : 0 : jl_safe_printf("%p: %s Stack frame %p -- %d of %d (%s)\n",
1382 : 0 : (void*)data, prefix, (void*)data->s, (int)data->i,
1383 : 0 : (int)data->nroots >> 1,
1384 [ # # ]: 0 : (data->nroots & 1) ? "indirect" : "direct");
1385 : : }
1386 [ # # ]: 0 : else if (pc == gc_mark_label_addrs[GC_MARK_L_module_binding]) {
1387 : : // module_binding
1388 : 0 : gc_mark_binding_t *data = gc_repush_markdata(&sp, gc_mark_binding_t);
1389 [ # # ]: 0 : if ((jl_gc_mark_data_t *)data > data_top) {
1390 : 0 : jl_safe_printf("Mark stack unwind overflow -- ABORTING !!!\n");
1391 : 0 : break;
1392 : : }
1393 : 0 : jl_safe_printf("%p: %s Module (bindings) %p (bits %d) -- [%p, %p)\n",
1394 : 0 : (void*)data, prefix, (void*)data->parent, (int)data->bits,
1395 : 0 : (void*)data->begin, (void*)data->end);
1396 : : }
1397 : : else {
1398 : 0 : jl_safe_printf("Unknown pc %p --- ABORTING !!!\n", pc);
1399 : 0 : break;
1400 : : }
1401 : : }
1402 : 0 : jl_set_safe_restore(old_buf);
1403 : : }
1404 : :
1405 : : static int gc_logging_enabled = 0;
1406 : :
1407 : 2 : JL_DLLEXPORT void jl_enable_gc_logging(int enable) {
1408 : 2 : gc_logging_enabled = enable;
1409 : 2 : }
1410 : :
1411 : 14024 : void _report_gc_finished(uint64_t pause, uint64_t freed, int full, int recollect) JL_NOTSAFEPOINT {
1412 [ + + ]: 14024 : if (!gc_logging_enabled) {
1413 : 14022 : return;
1414 : : }
1415 [ + + + + ]: 2 : jl_safe_printf("GC: pause %.2fms. collected %fMB. %s %s\n",
1416 : : pause/1e6, freed/1e6,
1417 : : full ? "full" : "incr",
1418 : : recollect ? "recollect" : ""
1419 : : );
1420 : : }
1421 : :
1422 : : #ifdef __cplusplus
1423 : : }
1424 : : #endif
|