pike.git / src / gc.c

version» Context lines:

pike.git/src/gc.c:23:   #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.135 2000/09/15 17:27:41 grubba Exp $"); + RCSID("$Id: gc.c,v 1.136 2000/09/30 16:01:39 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:88: Inside #if defined(GC_VERBOSE) && !defined(PIKE_DEBUG)
     #if defined(GC_VERBOSE) && !defined(PIKE_DEBUG)   #undef GC_VERBOSE   #endif   #ifdef GC_VERBOSE   #define GC_VERBOSE_DO(X) X   #else   #define GC_VERBOSE_DO(X)   #endif    - /* Kludge to avoid some loss of precision warnings. */ - #ifdef __ECL - static inline long CAST_TO_LONG(ptrdiff_t val) - { -  return DO_NOT_WARN((long)val); - } - #else /* !__ECL */ - #define CAST_TO_LONG(val) ((long)(val)) - #endif /* __ECL */ -  +    INT32 num_objects = 1; /* Account for empty_array. */   INT32 num_allocs =0;   ptrdiff_t alloc_threshold = MIN_ALLOC_THRESHOLD;   PMOD_EXPORT int Pike_in_gc = 0;   struct pike_queue gc_mark_queue;   time_t last_gc;      struct gc_frame   {    struct gc_frame *back; /* Previous stack frame. */
pike.git/src/gc.c:418:    fprintf(stderr,"%*s **In p->identifiers[%ld] (%s)\n",indent,"",    DO_NOT_WARN((long)e),    p->identifiers[e].name ? p->identifiers[e].name->str : "no name");    break;    }      #define FOO(NTYP,TYP,NAME) \    if(location == (void *)&p->NAME) fprintf(stderr,"%*s **In p->" #NAME "\n",indent,""); \    if(ptr >= (char *)p->NAME && ptr<(char*)(p->NAME+p->PIKE_CONCAT(num_,NAME))) \    fprintf(stderr,"%*s **In p->" #NAME "[%ld]\n",indent,"", \ -  CAST_TO_LONG(((char *)ptr - (char *)(p->NAME)) / sizeof(TYP))); +  PTRDIFF_T_TO_LONG(((char *)ptr - (char *)(p->NAME)) / sizeof(TYP)));   #include "program_areas.h"       break;    }       case T_OBJECT:    {    struct object *o=(struct object *)memblock;    struct program *p;   
pike.git/src/gc.c:507:    fprintf(stderr, "back=%p, prev=%p, next=%p, data=%p, cycle=%u, flags=0x%02x",    l->back, PREV(l), NEXT(l), l->data, CYCLE(l), l->frameflags);    else    fprintf(stderr, "LINK back=%p, data=%p, weak=%d, flags=0x%02x",    l->back, l->data, l->u.link.weak, l->frameflags);   }      static void describe_marker(struct marker *m)   {    if (m) { -  fprintf(stderr, "marker at %p: flags=0x%06x, refs=%d, weak=%d, " +  fprintf(stderr, "marker at %p: flags=0x%04x, refs=%d, weak=%d, "    "xrefs=%d, saved=%d, frame=%p",    m, m->flags, m->refs, m->weak_refs,    m->xrefs, m->saved_refs, m->frame);   #ifdef PIKE_DEBUG    if (m->frame) {    fputs(" [", stderr);    describe_gc_frame(m->frame);    putc(']', stderr);    }   #endif
pike.git/src/gc.c:543:    describe(a);    if (flags & 1) locate_references(a);    if (flags & 2)    fatal_after_gc = "Fatal in garbage collector.\n";    else    debug_fatal("Fatal in garbage collector.\n");   }      static void gdb_gc_stop_here(void *a, int weak)   { -  fprintf(stderr,"***One %sref found%s.\n", +  fprintf(stderr,"***One %sref found%s. ",    weak ? "weak " : "",    found_where?found_where:""); -  describe_something(found_in, found_in_type, 2, 1, DESCRIBE_NO_DMALLOC); -  describe_location(found_in , found_in_type, gc_svalue_location,2,1,0); +  describe_location(found_in , found_in_type, gc_svalue_location,0,1,0);    fprintf(stderr,"----------end------------\n");   }      void debug_gc_xmark_svalues(struct svalue *s, ptrdiff_t num, char *fromwhere)   {    found_in=(void *)fromwhere;    found_in_type=-1;    gc_xmark_svalues(s,num);    found_in_type=T_UNKNOWN;    found_in=0;
pike.git/src/gc.c:609:    found_in=data;    found_in_type=t;    ret=gc_check(x);    found_in_type=T_UNKNOWN;    found_in=0;    return ret;   }      /* Avoid loss of precision warning. */   #ifdef __ECL - static inline long SIZE_T_TO_LONG(size_t x) + static inline unsigned long SIZE_T_TO_ULONG(size_t x)   { -  return DO_NOT_WARN((long)x); +  return DO_NOT_WARN((unsigned long)x);   }   #else /* !__ECL */ - #define SIZE_T_TO_LONG(x) ((long)(x)) + #define SIZE_T_TO_ULONG(x) ((unsigned long)(x))   #endif /* __ECL */      void low_describe_something(void *a,    int t,    int indent,    int depth,    int flags)   {    struct program *p=(struct program *)a;    struct marker *m;
pike.git/src/gc.c:726:    fprintf(stderr,"%*s**identifiers:\n",indent,"");    for(e=0;e<p->num_identifier_references;e++)    fprintf(stderr,"%*s**** %s\n",indent,"",ID_FROM_INT(p,e)->name->str);       fprintf(stderr,"%*s**num inherits: %d\n",indent,"",p->num_inherits);    }       if(flags & DESCRIBE_MEM)    {   #define FOO(NUMTYPE,TYPE,NAME) \ -  fprintf(stderr, "%*s* " #NAME " %p[%ld]\n", \ -  indent, "", p->NAME, SIZE_T_TO_LONG(p->PIKE_CONCAT(num_,NAME))); +  fprintf(stderr, "%*s* " #NAME " %p[%lu]\n", \ +  indent, "", p->NAME, SIZE_T_TO_ULONG(p->PIKE_CONCAT(num_,NAME)));   #include "program_areas.h"    }       break;    }       case T_MULTISET:    fprintf(stderr,"%*s**Describing array of multiset:\n",indent,"");    debug_dump_array(((struct multiset *)a)->ind);    break;
pike.git/src/gc.c:897:    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 "    "got missed by gc_is_referenced().\n");    else if (!(m->flags & GC_DO_FREE))    gc_fatal(a, 2, "An unreferenced thing "    "got missed by gc_do_free().\n");    else if (m->flags & GC_GOT_EXTRA_REF)    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 to garb is still around. " -  "It's probably one with only external weak refs.\n"); -  else if (m->weak_refs < 0) +  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");    }    }    break;       default:    fatal("debug_gc_touch() used in invalid gc pass.\n");
pike.git/src/gc.c:1218:    "weak refs.\n");   #endif    m->weak_refs--;    goto should_free;    }    return 0;      should_free:    gc_ext_weak_refs--;   #ifdef PIKE_DEBUG +  m->saved_refs--;    m->flags |= GC_WEAK_FREED;   #endif       if (*(INT32 *) a == 1) {    /* Make sure the thing doesn't run out of refs, since we can't    * handle cascading frees now. We'll do it in the free pass    * instead. */    gc_add_extra_ref(a);    m->flags |= GC_GOT_DEAD_REF;    }
pike.git/src/gc.c:1244:    struct marker *m = get_marker(debug_malloc_pass(a));      #ifdef PIKE_DEBUG    if (!a) fatal("Got null pointer.\n");    if (Pike_in_gc != GC_PASS_MARK && Pike_in_gc != GC_PASS_ZAP_WEAK)    fatal("gc mark attempted in invalid pass.\n");    if (!*(INT32 *) a)    gc_fatal(a, 0, "Marked a thing without refs.\n");    if (m->weak_refs < 0)    gc_fatal(a, 0, "Marking thing scheduled for weak free.\n"); -  if (Pike_in_gc == GC_PASS_ZAP_WEAK && !(m->flags & GC_MARKED)) -  gc_fatal(a, 0, "gc_mark() called for thing in zap weak pass " -  "that wasn't marked before.\n"); +    #endif    -  if (Pike_in_gc == GC_PASS_ZAP_WEAK) +  if (Pike_in_gc == GC_PASS_ZAP_WEAK) {    /* Things are visited in the zap weak pass through the mark    * functions to free refs to internal things that only got weak    * external references. That happens only when a thing also have    * internal cyclic non-weak refs. */ -  + #ifdef PIKE_DEBUG +  if (!(m->flags & GC_MARKED)) +  gc_fatal(a, 0, "gc_mark() called for thing in zap weak pass " +  "that wasn't marked before.\n"); + #endif    if (m->flags & GC_FREE_VISITED)    return 0;    else {    m->flags |= GC_FREE_VISITED;    return 1;    } -  +  }       else if (m->flags & GC_MARKED) {   #ifdef PIKE_DEBUG    if (m->weak_refs != 0)    gc_fatal(a, 0, "weak_refs changed in marker already visited by gc_mark().\n");   #endif    return 0;    }    else {    if (m->weak_refs) {
pike.git/src/gc.c:1282:    }    m->flags = (m->flags & ~GC_NOT_REFERENCED) | GC_MARKED;    DO_IF_DEBUG(marked++);    return 1;    }   }      void gc_cycle_enqueue(gc_cycle_check_cb *checkfn, void *data, int weak)   {    struct gc_frame *l = alloc_gc_frame(); + #ifdef PIKE_DEBUG +  if (Pike_in_gc != GC_PASS_CYCLE) +  gc_fatal(data, 0, "Use of the gc frame stack outside the cycle check pass.\n"); + #endif   #ifdef GC_VERBOSE    if (++num_gc_frames > max_gc_frames) max_gc_frames = num_gc_frames;   #endif    l->data = data;    l->u.link.checkfn = checkfn;    l->u.link.weak = weak;    l->frameflags = 0;    l->back = gc_rec_top;   #ifdef GC_STACK_DEBUG    fprintf(stderr, "enqueue %p [%p]: ", l, gc_rec_top);    describe_gc_frame(l);    fputc('\n', stderr);   #endif    gc_rec_top = l;   }      static struct gc_frame *gc_cycle_enqueue_pop(void *data)   {    struct gc_frame *l = alloc_gc_frame(); -  + #ifdef PIKE_DEBUG +  if (Pike_in_gc != GC_PASS_CYCLE) +  gc_fatal(data, 0, "Use of the gc frame stack outside the cycle check pass.\n"); + #endif   #ifdef GC_VERBOSE    if (++num_gc_frames > max_gc_frames) max_gc_frames = num_gc_frames;   #endif    l->data = data;    PREV(l) = gc_rec_last;    NEXT(l) = 0;    CYCLE(l) = 0;    l->frameflags = GC_POP_FRAME;    l->back = gc_rec_top;   #ifdef GC_STACK_DEBUG    fprintf(stderr, "enqueue %p [%p]: ", l, gc_rec_top);    describe_gc_frame(l);    fputc('\n', stderr);   #endif    gc_rec_top = l;    return l;   }      void gc_cycle_run_queue()   { -  + #ifdef PIKE_DEBUG +  if (Pike_in_gc != GC_PASS_CYCLE) +  fatal("Use of the gc frame stack outside the cycle check pass.\n"); + #endif    while (gc_rec_top) {   #ifdef GC_STACK_DEBUG    fprintf(stderr, "dequeue %p [%p]: ", gc_rec_top, gc_rec_top->back);    describe_gc_frame(gc_rec_top);    fputc('\n', stderr);   #endif    if (gc_rec_top->frameflags & GC_POP_FRAME) {    struct gc_frame *l = gc_rec_top->back;    gc_cycle_pop(gc_rec_top->data);    gc_rec_top = l;
pike.git/src/gc.c:1362:   #define CYCLE_DEBUG_MSG(M, TXT) do {} while (0)   #endif      static void rotate_rec_list (struct gc_frame *beg, struct gc_frame *pos)   /* Rotates the marker list and the cycle stack so the bit from pos    * down to the end gets before the bit from beg down to pos. */   {    struct gc_frame *l;      #ifdef PIKE_DEBUG +  if (Pike_in_gc != GC_PASS_CYCLE) +  fatal("Use of the gc frame stack outside the cycle check pass.\n");    CHECK_POP_FRAME(beg);    CHECK_POP_FRAME(pos);    if (beg == pos)    gc_fatal(beg->data, 0, "Cycle already broken at requested position.\n");    if (NEXT(gc_rec_last))    gc_fatal(gc_rec_last->data, 0, "gc_rec_last not at end.\n");   #endif      #ifdef GC_STACK_DEBUG    fprintf(stderr,"Stack before:\n");
pike.git/src/gc.c:1540:       return 0;    }      #ifdef PIKE_DEBUG    if (weak < 0 && gc_rec_last->frameflags & GC_FOLLOWED_NONSTRONG)    gc_fatal(x, 0, "Followed strong link too late.\n");    if (weak >= 0) gc_rec_last->frameflags |= GC_FOLLOWED_NONSTRONG;   #endif    -  if (weak > 0) { - #ifdef PIKE_DEBUG -  if (m->weak_refs == 0) -  gc_fatal(x, 0, "Followed weak ref to thing that should have none left.\n"); -  /* We only keep m->weak_refs accurate in debug mode for the sake -  * of the checks in debug_gc_touch(); nothing else should trust it -  * to be valid after the mark pass. */ -  m->weak_refs--; /* Might already be negative. */ - #endif -  gc_ext_weak_refs--; -  } -  +     if (m->frame && !(m->frame->frameflags & GC_OFF_STACK)) {    /* A cyclic reference is found. */   #ifdef PIKE_DEBUG    if (gc_rec_last == &rec_list)    gc_fatal(x, 0, "Cyclic ref involves dummy rec_list marker.\n");    CHECK_POP_FRAME(gc_rec_last);    CHECK_POP_FRAME(m->frame);   #endif       if (m != last) {
pike.git/src/gc.c:1966:   #endif       last_gc=TIME(0);       multiplier=pow(MULTIPLIER, (double) num_allocs / (double) alloc_threshold);    objects_alloced*=multiplier;    objects_alloced += (double) num_allocs;       objects_freed*=multiplier;    -  /* Thread switches, object alloc/free and any reference changes -  * (except by the gc itself) are disallowed now. */ +  /* Thread switches, object alloc/free and any reference changes are +  * disallowed now. */      #ifdef PIKE_DEBUG    weak_freed = checked = marked = cycle_checked = live_ref = 0;    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();
pike.git/src/gc.c:2011: Inside #if defined(PIKE_DEBUG)
   if(builtin_constants)    gc_external_mark2(builtin_constants,0," &builtin_constants");    }   #endif       /* These callbacks are mainly for the check pass, but can also    * do things that are normally associated with the mark pass    */    call_callback(& gc_callbacks, (void *)0);    -  GC_VERBOSE_DO(fprintf(stderr, "| check: %u references checked\n", checked)); +  GC_VERBOSE_DO(fprintf(stderr, "| check: %u references checked, counted %lu weak refs\n", +  checked, SIZE_T_TO_ULONG(gc_ext_weak_refs)));       Pike_in_gc=GC_PASS_MARK;       /* Anything after and including gc_internal_foo in the linked lists    * are considered to lack external references. The mark pass move    * externally referenced things in front of these pointers. */    gc_internal_array = empty_array.next;    gc_internal_multiset = first_multiset;    gc_internal_mapping = first_mapping;    gc_internal_program = first_program;    gc_internal_object = first_object;    -  /* Next we mark anything with external references */ +  /* Next we mark anything with external references. Note that we can +  * follow the same reference several times, e.g. with shared mapping +  * data blocks. */    gc_mark_all_arrays();    run_queue(&gc_mark_queue);    gc_mark_all_multisets();    run_queue(&gc_mark_queue);    gc_mark_all_mappings();    run_queue(&gc_mark_queue);    gc_mark_all_programs();    run_queue(&gc_mark_queue);    gc_mark_all_objects();    run_queue(&gc_mark_queue);   #ifdef PIKE_DEBUG    if(gc_debug) gc_mark_all_strings();   #endif /* PIKE_DEBUG */       GC_VERBOSE_DO(fprintf(stderr, -  "| mark: %u markers referenced,\n" -  "| %u weak references freed, %d things really freed\n", -  marked, weak_freed, objs - num_objects)); +  "| mark: %u markers referenced, %u weak references freed,\n" +  "| %d things really freed, got %lu tricky weak refs\n", +  marked, weak_freed, objs - num_objects, +  SIZE_T_TO_ULONG(gc_ext_weak_refs)));    -  Pike_in_gc=GC_PASS_CYCLE; +  {   #ifdef PIKE_DEBUG -  +  size_t orig_ext_weak_refs = gc_ext_weak_refs;    obj_count = num_objects;    max_gc_frames = 0;   #endif -  +  Pike_in_gc=GC_PASS_CYCLE;    -  /* Now find all cycles in the internal structures */ +  /* Now find all cycles in the internal structures. Note that we can +  * follow the same reference several times, just like in the mark +  * pass. */    /* Note: The order between types here is normally not significant,    * but the permuting destruct order tests in the testsuite won't be    * really effective unless objects are handled first. :P */    gc_cycle_check_all_objects();    gc_cycle_check_all_arrays();    gc_cycle_check_all_multisets();    gc_cycle_check_all_mappings();    gc_cycle_check_all_programs();      #ifdef PIKE_DEBUG    if (gc_rec_top)    fatal("gc_rec_top not empty at end of cycle check pass.\n");    if (NEXT(&rec_list) || gc_rec_last != &rec_list || gc_rec_top)    fatal("Recurse list not empty or inconsistent after cycle check pass.\n"); -  +  if (gc_ext_weak_refs != orig_ext_weak_refs) +  fatal("gc_ext_weak_refs changed from %lu to %lu in cycle check pass.\n", +  SIZE_T_TO_ULONG(orig_ext_weak_refs), SIZE_T_TO_ULONG(gc_ext_weak_refs));   #endif    -  +  GC_VERBOSE_DO(fprintf(stderr, +  "| cycle: %u internal things visited, %u cycle ids used,\n" +  "| %u weak references freed, %d things really freed,\n" +  "| space for %u gc frames used\n", +  cycle_checked, last_cycle, weak_freed, +  obj_count - num_objects, max_gc_frames)); +  } +    #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();
pike.git/src/gc.c:2099: Inside #if defined(PIKE_DEBUG) and #if defined(DEBUG_MALLOC)
   debug_malloc_dump_references(m, 2, 1, 0);    fprintf(stderr, "Describing thing for marker:\n");    describe(m->data);    fatal("Fatal in garbage collector.\n");    }   #endif    GC_VERBOSE_DO(fprintf(stderr, "| middletouch\n"));    }   #endif    -  GC_VERBOSE_DO(fprintf(stderr, -  "| cycle: %u internal things visited, %u cycle ids used,\n" -  "| %u weak references freed, %d things really freed,\n" -  "| space for %u gc frames used\n", -  cycle_checked, last_cycle, weak_freed, obj_count - num_objects, -  max_gc_frames)); -  +     if (gc_ext_weak_refs) {    size_t to_free = gc_ext_weak_refs;   #ifdef PIKE_DEBUG    obj_count = num_objects;   #endif    Pike_in_gc = GC_PASS_ZAP_WEAK;    /* Zap weak references from external to internal things. That    * doesn't occur very often; only when something have both    * external weak refs and nonweak cyclic refs from internal    * things. */    gc_zap_ext_weak_refs_in_mappings(); -  if (gc_ext_weak_refs) { +     gc_zap_ext_weak_refs_in_arrays();    /* Multisets handled as arrays. */ -  if (gc_ext_weak_refs) { +     gc_zap_ext_weak_refs_in_objects(); -  if (gc_ext_weak_refs) +     gc_zap_ext_weak_refs_in_programs(); -  } -  } +     GC_VERBOSE_DO(    fprintf(stderr, -  "| zap weak: freed %u external weak refs, %d things really freed\n", -  to_free - gc_ext_weak_refs, obj_count - num_objects)); +  "| zap weak: freed %ld external weak refs, %lu internal still around,\n" +  "| %d things really freed\n", +  PTRDIFF_T_TO_LONG(to_free - gc_ext_weak_refs), +  SIZE_T_TO_ULONG(gc_ext_weak_refs), obj_count - num_objects));    }       /* Thread switches, object alloc/free and reference changes are    * allowed again now. */       Pike_in_gc=GC_PASS_FREE;   #ifdef PIKE_DEBUG    weak_freed = 0;    obj_count = num_objects;   #endif
pike.git/src/gc.c:2221: Inside #if defined(PIKE_DEBUG)
   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)    fatal("Object count wrong after gc; expected %d, got %d.\n", num_objects, n);    GC_VERBOSE_DO(fprintf(stderr, "| posttouch: %u things\n", n)); -  if(fatal_after_gc) fatal("%s", fatal_after_gc); +     }    if (gc_extra_refs)    fatal("Lost track of %d extra refs to things in gc.\n", gc_extra_refs); -  if (gc_ext_weak_refs) -  fatal("Still got %lu external weak references to internal things in gc.\n", -  (unsigned long)PTRDIFF_T_TO_LONG(gc_ext_weak_refs)); +  if(fatal_after_gc) 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    * then. */    if (pre_kill_objs < num_objects) objs -= pre_kill_objs;    else objs -= num_objects;