LCOV - code coverage report
Current view: top level - src - gc-debug.c (source / functions) Hit Total Coverage
Test: [build process] commit ef510b1f346f4c9f9d86eaceace5ca54961a1dbc Lines: 7 223 3.1 %
Date: 2022-07-17 01:01:28 Functions: 3 16 18.8 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 1 110 0.9 %

           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                 :          0 : void jl_gc_debug_critical_error(void)
     610                 :            : {
     611                 :          0 : }
     612                 :            : 
     613                 :          0 : void jl_gc_debug_print_status(void)
     614                 :            : {
     615                 :            :     // May not be accurate but should be helpful enough
     616                 :          0 :     uint64_t pool_count = gc_num.poolalloc;
     617                 :          0 :     uint64_t big_count = gc_num.bigalloc;
     618                 :          0 :     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                 :          0 : }
     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                 :         15 : void jl_print_gc_stats(JL_STREAM *s)
     844                 :            : {
     845                 :         15 : }
     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                 :         15 : 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                 :         15 : }
    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                 :          0 : JL_DLLEXPORT void jl_enable_gc_logging(int enable) {
    1408                 :          0 :     gc_logging_enabled = enable;
    1409                 :          0 : }
    1410                 :            : 
    1411                 :       1179 : void _report_gc_finished(uint64_t pause, uint64_t freed, int full, int recollect) JL_NOTSAFEPOINT {
    1412         [ +  - ]:       1179 :     if (!gc_logging_enabled) {
    1413                 :       1179 :         return;
    1414                 :            :     }
    1415   [ #  #  #  # ]:          0 :     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

Generated by: LCOV version 1.14