pike.git / src / gc.c

version» Context lines:

pike.git/src/gc.c:1:   /*   || This file is part of Pike. For copyright information see COPYRIGHT.   || Pike is distributed under GPL, LGPL and MPL. See the file COPYING   || for more information. - || $Id: gc.c,v 1.191 2003/01/12 17:25:39 mast Exp $ + || $Id: gc.c,v 1.192 2003/02/01 15:37:23 mast Exp $   */      #include "global.h"      struct callback *gc_evaluator_callback=0;      #include "array.h"   #include "multiset.h"   #include "mapping.h"   #include "object.h"
pike.git/src/gc.c:24:   #include "constants.h"   #include "interpret.h"   #include "bignum.h"      #include "gc.h"   #include "main.h"   #include <math.h>      #include "block_alloc.h"    - RCSID("$Id: gc.c,v 1.191 2003/01/12 17:25:39 mast Exp $"); + RCSID("$Id: gc.c,v 1.192 2003/02/01 15:37:23 mast Exp $");      /* Run garbage collect approximately every time    * 20 percent of all arrays, objects and programs is    * garbage.    */      #define GC_CONST 20   #define MIN_ALLOC_THRESHOLD 1000   #define MAX_ALLOC_THRESHOLD 10000000   #define MULTIPLIER 0.9
pike.git/src/gc.c:95:   #define GC_VERBOSE_DO(X)   #endif      int num_objects = 3; /* Account for *_empty_array. */   int num_allocs =0;   ptrdiff_t alloc_threshold = MIN_ALLOC_THRESHOLD;   PMOD_EXPORT int Pike_in_gc = 0;   int gc_generation = 0;   struct pike_queue gc_mark_queue;   time_t last_gc; + int gc_trace = 0, gc_debug = 0;      struct gc_frame   {    struct gc_frame *back; /* Previous stack frame. */    void *data;    union {    struct { /* Pop frame. */    struct gc_frame *prev; /* Previous frame in rec_list. */    struct gc_frame *next; /* Next pointer in rec_list and kill_list. */    unsigned INT16 cycle; /* Cycle id number. */
pike.git/src/gc.c:312:       return PIKE_T_UNKNOWN;   }      void *check_for =0;   static char *found_where="";   static void *found_in=0;   static int found_in_type=0;   void *gc_svalue_location=0;   char *fatal_after_gc=0; - int gc_debug = 0; +       #define DESCRIBE_MEM 1   #define DESCRIBE_NO_REFS 2   #define DESCRIBE_SHORT 4   #define DESCRIBE_NO_DMALLOC 8      /* type == -1 means that memblock is a char* and should be    * really be printed..    */   void describe_location(void *real_memblock,
pike.git/src/gc.c:616: Inside #if defined(PIKE_DEBUG)
   describe_gc_frame(m->frame);    putc(']', stderr);    }   #endif    putc('\n', stderr);    }    else    fprintf(stderr, "no marker\n");   }    + #endif /* PIKE_DEBUG */ +    void debug_gc_fatal(void *a, int flags, const char *fmt, ...)   {    struct marker *m;    int orig_gc_pass = Pike_in_gc;    va_list args;       va_start(args, fmt);       fprintf(stderr, "**");    (void) VFPRINTF(stderr, fmt, args);       va_end(args);    -  + #ifdef PIKE_DEBUG    if (a) {    /* Temporarily jumping out of gc to avoid being catched in debug    * checks in describe(). */    Pike_in_gc = 0;    describe(a);       if (flags & 1) locate_references(a);       m=find_marker(a);    if(m)
pike.git/src/gc.c:651:    describe(m);    }else{    fprintf(stderr,"** No marker found for this thing.\n");    }    Pike_in_gc = orig_gc_pass;    }       if (flags & 2)    fatal_after_gc = "Fatal in garbage collector.\n";    else + #endif    debug_fatal("Fatal in garbage collector.\n");   }    -  + #ifdef PIKE_DEBUG +    static void gdb_gc_stop_here(void *a, int weak)   {   #if 0    if (!found_where) Pike_fatal("found_where is zero.\n");   #endif    fprintf(stderr,"***One %sref found%s. ",    weak ? "weak " : "",    found_where?found_where:"");    if (found_in) {    if (gc_svalue_location)
pike.git/src/gc.c:1106:    m = get_marker(a);    if (!(m->flags & GC_WATCHED)) {    m->flags |= GC_WATCHED;    fprintf(stderr, "## Watching thing %p.\n", a);    gc_is_watching++;    }    else    fprintf(stderr, "## Already watching thing %p.\n", a);   }    + #endif /* PIKE_DEBUG */ +    void debug_gc_touch(void *a)   {    struct marker *m;    -  + #ifdef PIKE_DEBUG    if (gc_is_watching && (m = find_marker(a)) && m->flags & GC_WATCHED) {    /* This is useful to set breakpoints on. */    fprintf(stderr, "## Watched thing %p found in "    "gc_touch() in pass %d.\n", a, Pike_in_gc);    } -  + #endif       if (!a) Pike_fatal("Got null pointer.\n");       switch (Pike_in_gc) {    case GC_PASS_PRETOUCH:    m = find_marker(a); -  if (m && !(m->flags & (GC_PRETOUCHED|GC_WATCHED))) +  if (m && !(m->flags & (GC_PRETOUCHED + #ifdef PIKE_DEBUG +  |GC_WATCHED + #endif +  )))    gc_fatal(a, 1, "Thing got an existing but untouched marker.\n");    m = get_marker(a);    m->flags |= GC_PRETOUCHED; -  + #ifdef PIKE_DEBUG    m->saved_refs = *(INT32 *) a; -  + #endif    break;       case GC_PASS_MIDDLETOUCH: {    int extra_ref;    m = find_marker(a);    if (!m)    gc_fatal(a, 1, "Found a thing without marker.\n");    else if (!(m->flags & GC_PRETOUCHED))    gc_fatal(a, 1, "Thing got an existing but untouched marker.\n"); -  + #ifdef PIKE_DEBUG    extra_ref = (m->flags & GC_GOT_EXTRA_REF) == GC_GOT_EXTRA_REF;    if (m->saved_refs + extra_ref < *(INT32 *) a)    if (m->flags & GC_WEAK_FREED)    gc_fatal(a, 1, "Something failed to remove weak reference(s) to thing, "    "or it has gotten more references since gc start.\n");    else    gc_fatal(a, 1, "Thing has gotten more references since gc start.\n"); -  else if (m->weak_refs > m->saved_refs) +  else +  if (m->weak_refs > m->saved_refs)    gc_fatal(a, 0, "A thing got more weak references than references.\n"); -  + #endif    m->flags |= GC_MIDDLETOUCHED;    break;    }       case GC_PASS_POSTTOUCH:    m = find_marker(a);    if (!*(INT32 *) a)    gc_fatal(a, 1, "Found a thing without refs.\n");    if (m) {    if (!(m->flags & (GC_PRETOUCHED|GC_MIDDLETOUCHED)))    gc_fatal(a, 2, "An existing but untouched marker found "    "for object in linked lists.\n");    else if (m->flags & GC_LIVE_RECURSE ||    (m->frame && m->frame->frameflags & (GC_WEAK_REF|GC_STRONG_REF)))    gc_fatal(a, 2, "Thing still got flag from recurse list.\n"); -  + #ifdef PIKE_DEBUG    else if (m->flags & GC_MARKED)    return;    else if (!(m->flags & GC_NOT_REFERENCED) || m->flags & GC_XREFERENCED)    gc_fatal(a, 3, "A thing with external references "    "got missed by mark pass.\n");    else if (!(m->flags & GC_CYCLE_CHECKED))    gc_fatal(a, 2, "A thing was missed by "    "both mark and cycle check pass.\n");    else if (!(m->flags & GC_IS_REFERENCED))    gc_fatal(a, 2, "An unreferenced thing "
pike.git/src/gc.c:1184:    gc_fatal(a, 2, "A thing still got an extra ref.\n");    else if (m->weak_refs > m->saved_refs)    gc_fatal(a, 2, "A thing got more weak references than references.\n");    else if (!(m->flags & GC_LIVE)) {    if (m->weak_refs < 0)    gc_fatal(a, 3, "A thing which had only weak references is "    "still around after gc.\n");    else    gc_fatal(a, 3, "A thing to garb is still around.\n");    } + #endif    }    break;       default:    Pike_fatal("debug_gc_touch() used in invalid gc pass.\n");    }   }    -  + #ifdef PIKE_DEBUG +    static INLINE struct marker *gc_check_debug(void *a, int weak)   {    struct marker *m;       if (!a) Pike_fatal("Got null pointer.\n");    if(Pike_in_gc == GC_PASS_LOCATE)    {    if(check_for == a)    {    gdb_gc_stop_here(a, weak);
pike.git/src/gc.c:2390:    }       CALL_AND_UNSET_ONERROR(tmp);   }      int do_gc(void)   {    double tmp;    ptrdiff_t start_num_objs, objs, pre_kill_objs;    double multiplier; - #ifdef PIKE_DEBUG +    #ifdef HAVE_GETHRTIME    hrtime_t gcstarttime = 0;   #endif -  + #ifdef PIKE_DEBUG    unsigned destroy_count, obj_count;    ONERROR uwp;   #endif       if(Pike_in_gc) return 0;   #ifdef DEBUG_MALLOC    if(debug_options & GC_RESET_DMALLOC)    reset_debug_malloc();   #endif    init_gc();    gc_generation++;    Pike_in_gc=GC_PASS_PREPARE; - #ifdef PIKE_DEBUG +     gc_debug = d_flag; -  + #ifdef PIKE_DEBUG    SET_ONERROR(uwp, fatal_on_error, "Shouldn't get an exception inside the gc.\n");    if (gc_is_watching)    fprintf(stderr, "## Doing gc while watching for %d things.\n", gc_is_watching);   #endif       destruct_objects_to_destruct();       if(gc_evaluator_callback)    {    remove_callback(gc_evaluator_callback);    gc_evaluator_callback=0;    }       objs=num_objects;    last_cycle = 0;    - #ifdef PIKE_DEBUG -  if(GC_VERBOSE_DO(1 ||) t_flag) { +  if(GC_VERBOSE_DO(1 ||) gc_trace) {    fprintf(stderr,"Garbage collecting ... ");    GC_VERBOSE_DO(fprintf(stderr, "\n"));   #ifdef HAVE_GETHRTIME    gcstarttime = gethrtime();   #endif    } -  + #ifdef PIKE_DEBUG    if(num_objects < 0)    Pike_fatal("Panic, less than zero objects!\n");   #endif       last_gc=TIME(0);       multiplier=pow(MULTIPLIER, (double) num_allocs / (double) alloc_threshold);    objects_alloced*=multiplier;    objects_alloced += (double) num_allocs * (1.0 - multiplier);       objects_freed*=multiplier;       start_num_objs = num_objects;       /* Thread switches, object alloc/free and any reference changes are    * disallowed now. */      #ifdef PIKE_DEBUG    delayed_freed = weak_freed = checked = marked = cycle_checked = live_ref = 0;    live_rec = frame_rot = 0; -  + #endif    if (gc_debug) {    unsigned n;    Pike_in_gc = GC_PASS_PRETOUCH;    n = gc_touch_all_arrays();    n += gc_touch_all_multisets();    n += gc_touch_all_mappings();    n += gc_touch_all_programs();    n += gc_touch_all_objects(); -  + #ifdef PIKE_DEBUG    gc_touch_all_strings(); -  + #endif    if (n != (unsigned) num_objects)    Pike_fatal("Object count wrong before gc; expected %d, got %d.\n", num_objects, n);    GC_VERBOSE_DO(fprintf(stderr, "| pretouch: %u things\n", n));    } - #endif +        Pike_in_gc=GC_PASS_CHECK;    gc_ext_weak_refs = 0;    /* First we count internal references */    gc_check_all_arrays();    gc_check_all_multisets();    gc_check_all_mappings();    gc_check_all_programs();    gc_check_all_objects();   
pike.git/src/gc.c:2599:    gc_zap_ext_weak_refs_in_programs();    GC_VERBOSE_DO(    fprintf(stderr,    "| zap weak: freed %"PRINTPTRDIFFT"d external weak refs, "    "%"PRINTSIZET"u internal still around,\n"    "| %d more things to free\n",    to_free - gc_ext_weak_refs, gc_ext_weak_refs,    delayed_freed - obj_count));    }    - #ifdef PIKE_DEBUG +     if (gc_debug) {    unsigned n;    size_t i;    struct marker *m;    Pike_in_gc=GC_PASS_MIDDLETOUCH;    n = gc_touch_all_arrays();    n += gc_touch_all_multisets();    n += gc_touch_all_mappings();    n += gc_touch_all_programs();    n += gc_touch_all_objects(); -  + #ifdef PIKE_DEBUG    gc_touch_all_strings(); -  + #endif    if (n != (unsigned) num_objects)    Pike_fatal("Object count wrong in gc; expected %d, got %d.\n", num_objects, n);    get_marker(rec_list.data)->flags |= GC_MIDDLETOUCHED;   #if 0 /* Temporarily disabled - Hubbe */ -  + #ifdef PIKE_DEBUG   #ifdef DEBUG_MALLOC    PTR_HASH_LOOP(marker, i, m)    if (!(m->flags & (GC_MIDDLETOUCHED|GC_WEAK_FREED)) &&    dmalloc_is_invalid_memory_block(m->data)) {    fprintf(stderr, "Found a stray marker after middletouch pass: ");    describe_marker(m);    fprintf(stderr, "Describing marker location(s):\n");    debug_malloc_dump_references(m, 2, 1, 0);    fprintf(stderr, "Describing thing for marker:\n");    Pike_in_gc = 0;    describe(m->data);    Pike_in_gc = GC_PASS_MIDDLETOUCH;    Pike_fatal("Fatal in garbage collector.\n");    }   #endif   #endif -  + #endif    GC_VERBOSE_DO(fprintf(stderr, "| middletouch\n"));    } - #endif +        /* Add an extra reference to the stuff gc_internal_* point to, so we    * know they're still around when gc_free_all_unreferenced_* are    * about to be called. */    if (gc_internal_array != &weak_empty_array) add_ref(gc_internal_array);    if (gc_internal_multiset) add_ref(gc_internal_multiset);    if (gc_internal_mapping) add_ref(gc_internal_mapping);    if (gc_internal_program) add_ref(gc_internal_program);    if (gc_internal_object) add_ref(gc_internal_object);   
pike.git/src/gc.c:2757:    GC_VERBOSE_DO(fprintf(stderr, "| kill: %u objects killed, %d things really freed\n",    destroy_count, pre_kill_objs - num_objects));       Pike_in_gc=GC_PASS_DESTRUCT;    /* Destruct objects on the destruct queue. */    GC_VERBOSE_DO(obj_count = num_objects);    destruct_objects_to_destruct();    GC_VERBOSE_DO(fprintf(stderr, "| destruct: %d things really freed\n",    obj_count - num_objects));    - #ifdef PIKE_DEBUG +     if (gc_debug) {    unsigned n;    Pike_in_gc=GC_PASS_POSTTOUCH;    n = gc_touch_all_arrays();    n += gc_touch_all_multisets();    n += gc_touch_all_mappings();    n += gc_touch_all_programs();    n += gc_touch_all_objects();    /* gc_touch_all_strings(); */    if (n != (unsigned) num_objects)    Pike_fatal("Object count wrong after gc; expected %d, got %d.\n", num_objects, n);    GC_VERBOSE_DO(fprintf(stderr, "| posttouch: %u things\n", n));    } -  + #ifdef PIKE_DEBUG    if (gc_extra_refs)    Pike_fatal("Lost track of %d extra refs to things in gc.\n", gc_extra_refs);    if(fatal_after_gc) Pike_fatal("%s", fatal_after_gc);   #endif       Pike_in_gc=0;    exit_gc();       /* It's possible that more things got allocated in the kill pass    * than were freed. The count before that is a better measurement
pike.git/src/gc.c:2804:    tmp = (double)MIN_ALLOC_THRESHOLD;    if(tmp > MAX_ALLOC_THRESHOLD)    tmp = (double)MAX_ALLOC_THRESHOLD;       alloc_threshold = (ptrdiff_t)tmp;       num_allocs=0;      #ifdef PIKE_DEBUG    UNSET_ONERROR (uwp); -  if(GC_VERBOSE_DO(1 ||) t_flag) +  if (max_gc_frames > max_tot_gc_frames) max_tot_gc_frames = max_gc_frames; +  tot_cycle_checked += cycle_checked; +  tot_live_rec += live_rec, tot_frame_rot += frame_rot; + #endif +  +  if(GC_VERBOSE_DO(1 ||) gc_trace)    {   #ifdef HAVE_GETHRTIME    fprintf(stderr,    "done (freed %"PRINTPTRDIFFT"d of %"PRINTPTRDIFFT"d things), %ld ms.\n",    objs, start_num_objs, (long)((gethrtime() - gcstarttime)/1000000));   #else    fprintf(stderr,    "done (freed %"PRINTPTRDIFFT"d of %"PRINTPTRDIFFT"d things)\n",    objs, start_num_objs);   #endif    } -  if (max_gc_frames > max_tot_gc_frames) max_tot_gc_frames = max_gc_frames; -  tot_cycle_checked += cycle_checked; -  tot_live_rec += live_rec, tot_frame_rot += frame_rot; - #endif +       #ifdef ALWAYS_GC    ADD_GC_CALLBACK();   #else    if(d_flag > 3) ADD_GC_CALLBACK();   #endif       return objs;   }