PageRenderTime 40ms CodeModel.GetById 16ms app.highlight 19ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/gc/backgraph.c

http://github.com/feyeleanor/RubyGoLightly
C | 475 lines | 335 code | 44 blank | 96 comment | 96 complexity | 49192b819842ef56c3c372456494e460 MD5 | raw file
  1/*
  2 * Copyright (c) 2001 by Hewlett-Packard Company. All rights reserved.
  3 *
  4 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
  5 * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
  6 *
  7 * Permission is hereby granted to use or copy this program
  8 * for any purpose,  provided the above notices are retained on all copies.
  9 * Permission to modify the code and to distribute modified code is granted,
 10 * provided the above notices are retained, and a notice that the code was
 11 * modified is included with the above copyright notice.
 12 *
 13 */
 14
 15/*
 16 * This implements a full, though not well-tuned, representation of the
 17 * backwards points-to graph.  This is used to test for non-GC-robust
 18 * data structures; the code is not used during normal garbage collection.
 19 *
 20 * One restriction is that we drop all back-edges from nodes with very
 21 * high in-degree, and simply add them add them to a list of such
 22 * nodes.  They are then treated as permanent roots.  Id this by itself
 23 * doesn't introduce a space leak, then such nodes can't contribute to
 24 * a growing space leak.
 25 */
 26
 27#ifdef MAKE_BACK_GRAPH
 28
 29#define MAX_IN	10	/* Maximum in-degree we handle directly */
 30
 31#include "private/dbg_mlc.h"
 32#include <unistd.h>
 33
 34#if !defined(DBG_HDRS_ALL) || (ALIGNMENT != CPP_WORDSZ/8) || !defined(UNIX_LIKE)
 35# error Configuration doesnt support MAKE_BACK_GRAPH
 36#endif
 37
 38/* We store single back pointers directly in the object's oh_bg_ptr field.   */
 39/* If there is more than one ptr to an object, we store q | FLAG_MANY, 	     */
 40/* where q is a pointer to a back_edges object.				     */
 41/* Every once in a while we use a back_edges object even for a single	     */
 42/* pointer, since we need the other fields in the back_edges structure to    */
 43/* be present in some fraction of the objects.  Otherwise we get serious     */
 44/* performance issues.							     */
 45#define FLAG_MANY 2
 46
 47typedef struct back_edges_struct {
 48  word n_edges;	/* Number of edges, including those in continuation	*/
 49  		/* structures.						*/
 50  unsigned short flags;
 51#	define RETAIN 1	/* Directly points to a reachable object;	*/
 52  			/* retain for next GC.				*/
 53  unsigned short height_gc_no;
 54  		/* If height > 0, then the GC_gc_no value when it	*/
 55  		/* was computed.  If it was computed this cycle, then	*/
 56  		/* it is current.  If it was computed during the	*/
 57  		/* last cycle, then it represents the old height,	*/
 58  		/* which is only saved for live objects referenced by	*/
 59  		/* dead ones.  This may grow due to refs from newly	*/
 60		/* dead objects.				        */
 61  signed_word height;
 62  		/* Longest path through unreachable nodes to this node	*/
 63  		/* that we found using depth first search.		*/
 64  
 65#   define HEIGHT_UNKNOWN ((signed_word)(-2))
 66#   define HEIGHT_IN_PROGRESS ((signed_word)(-1))
 67  ptr_t edges[MAX_IN];
 68  struct back_edges_struct *cont;
 69  		/* Pointer to continuation structure; we use only the	*/
 70  		/* edges field in the continuation.			*/
 71  		/* also used as free list link.				*/
 72} back_edges;
 73
 74/* Allocate a new back edge structure.  Should be more sophisticated	*/
 75/* if this were production code.					*/
 76#define MAX_BACK_EDGE_STRUCTS 100000
 77static back_edges *back_edge_space = 0;
 78int GC_n_back_edge_structs = 0;	/* Serves as pointer to never used	*/
 79				/* back_edges space.			*/
 80static back_edges *avail_back_edges = 0;
 81				/* Pointer to free list of deallocated	*/
 82				/* back_edges structures.		*/
 83
 84static back_edges * new_back_edges(void)
 85{
 86  if (0 == back_edge_space) {
 87    back_edge_space = (back_edges *)
 88	    		GET_MEM(MAX_BACK_EDGE_STRUCTS*sizeof(back_edges));
 89    GC_add_to_our_memory((ptr_t)back_edge_space,
 90    			 MAX_BACK_EDGE_STRUCTS*sizeof(back_edges));
 91  }
 92  if (0 != avail_back_edges) {
 93    back_edges * result = avail_back_edges;
 94    avail_back_edges = result -> cont;
 95    result -> cont = 0;
 96    return result;
 97  }
 98  if (GC_n_back_edge_structs >= MAX_BACK_EDGE_STRUCTS - 1) {
 99    ABORT("needed too much space for back edges: adjust "
100	  "MAX_BACK_EDGE_STRUCTS");
101  }
102  return back_edge_space + (GC_n_back_edge_structs++);
103}
104
105/* Deallocate p and its associated continuation structures.	*/
106static void deallocate_back_edges(back_edges *p)
107{
108   back_edges *last = p;
109
110   while (0 != last -> cont) last = last -> cont;
111   last -> cont = avail_back_edges;
112   avail_back_edges = p;
113}
114
115/* Table of objects that are currently on the depth-first search	*/
116/* stack.  Only objects with in-degree one are in this table.		*/
117/* Other objects are identified using HEIGHT_IN_PROGRESS.		*/
118/* FIXME: This data structure NEEDS IMPROVEMENT.			*/
119#define INITIAL_IN_PROGRESS 10000
120static ptr_t * in_progress_space = 0;
121static size_t in_progress_size = 0;
122static size_t n_in_progress = 0;
123
124static void push_in_progress(ptr_t p)
125{
126  if (n_in_progress >= in_progress_size) 
127    if (in_progress_size == 0) {
128      in_progress_size = INITIAL_IN_PROGRESS;
129      in_progress_space = (ptr_t *)GET_MEM(in_progress_size * sizeof(ptr_t));
130      GC_add_to_our_memory((ptr_t)in_progress_space,
131      			   in_progress_size * sizeof(ptr_t));
132    } else {
133      ptr_t * new_in_progress_space;
134      in_progress_size *= 2;
135      new_in_progress_space = (ptr_t *)
136	      			GET_MEM(in_progress_size * sizeof(ptr_t));
137      GC_add_to_our_memory((ptr_t)new_in_progress_space,
138      			   in_progress_size * sizeof(ptr_t));
139      BCOPY(in_progress_space, new_in_progress_space,
140	    n_in_progress * sizeof(ptr_t));
141      in_progress_space = new_in_progress_space;
142      /* FIXME: This just drops the old space.	*/
143    }
144  if (in_progress_space == 0)
145      ABORT("MAKE_BACK_GRAPH: Out of in-progress space: "
146	    "Huge linear data structure?");
147  in_progress_space[n_in_progress++] = p;
148}
149
150static GC_bool is_in_progress(ptr_t p)
151{
152  int i;
153  for (i = 0; i < n_in_progress; ++i) {
154    if (in_progress_space[i] == p) return TRUE;
155  }
156  return FALSE;
157}
158
159static void pop_in_progress(ptr_t p)
160{
161  --n_in_progress;
162  GC_ASSERT(in_progress_space[n_in_progress] == p);
163}
164
165#define GET_OH_BG_PTR(p) \
166		(ptr_t)REVEAL_POINTER(((oh *)(p)) -> oh_bg_ptr)
167#define SET_OH_BG_PTR(p,q) (((oh *)(p)) -> oh_bg_ptr) = HIDE_POINTER(q)
168
169/* Execute s once for each predecessor q of p in the points-to graph. 	*/
170/* s should be a bracketed statement.  We declare q.			*/
171#define FOR_EACH_PRED(q, p, s) \
172  { \
173    ptr_t q = GET_OH_BG_PTR(p); \
174    if (!((word)q & FLAG_MANY)) { \
175      if (q && !((word)q & 1)) s \
176	      /* !((word)q & 1) checks for a misnterpreted freelist link */ \
177    } else { \
178      back_edges *orig_be_ = (back_edges *)((word)q & ~FLAG_MANY); \
179      back_edges *be_ = orig_be_; \
180      int total_, local_; \
181      int n_edges_ = be_ -> n_edges; \
182      for (total_ = 0, local_ = 0; total_ < n_edges_; ++local_, ++total_) { \
183	  if (local_ == MAX_IN) { \
184	      be_ = be_ -> cont; \
185	      local_ = 0; \
186	  } \
187	  q = be_ -> edges[local_]; s \
188      } \
189    } \
190  }
191
192/* Ensure that p has a back_edges structure associated with it.	*/
193static void ensure_struct(ptr_t p)
194{
195  ptr_t old_back_ptr = GET_OH_BG_PTR(p);
196
197  if (!((word)old_back_ptr & FLAG_MANY)) {
198    back_edges *be = new_back_edges();
199    be -> flags = 0;
200    if (0 == old_back_ptr) {
201      be -> n_edges = 0;
202    } else {
203      be -> n_edges = 1;
204      be -> edges[0] = old_back_ptr;
205    }
206    be -> height = HEIGHT_UNKNOWN;
207    be -> height_gc_no = GC_gc_no - 1;
208    GC_ASSERT(be >= back_edge_space);
209    SET_OH_BG_PTR(p, (word)be | FLAG_MANY);
210  }
211}
212
213/* Add the (forward) edge from p to q to the backward graph.  Both p	*/
214/* q are pointers to the object base, i.e. pointers to an oh.		*/
215static void add_edge(ptr_t p,  ptr_t q)
216{
217    ptr_t old_back_ptr = GET_OH_BG_PTR(q);
218    back_edges * be, *be_cont;
219    word i;
220    static unsigned random_number = 13;
221#   define GOT_LUCKY_NUMBER (((++random_number) & 0x7f) == 0)
222      /* A not very random number we use to occasionally allocate a	*/
223      /* back_edges structure even for a single backward edge.  This	*/
224      /* prevents us from repeatedly tracing back through very long	*/
225      /* chains, since we will have some place to store height and	*/
226      /* in_progress flags along the way.				*/
227
228    GC_ASSERT(p == GC_base(p) && q == GC_base(q));
229    if (!GC_HAS_DEBUG_INFO(q) || !GC_HAS_DEBUG_INFO(p)) {
230      /* This is really a misinterpreted free list link, since we saw */
231      /* a pointer to a free list.  Dont overwrite it!		      */
232      return;
233    }
234    if (0 == old_back_ptr) {
235	SET_OH_BG_PTR(q, p);
236	if (GOT_LUCKY_NUMBER) ensure_struct(q);
237	return;
238    }
239    /* Check whether it was already in the list of predecessors. */
240      FOR_EACH_PRED(pred, q, { if (p == pred) return; });
241    ensure_struct(q);
242    old_back_ptr = GET_OH_BG_PTR(q);
243    be = (back_edges *)((word)old_back_ptr & ~FLAG_MANY);
244    for (i = be -> n_edges, be_cont = be; i > MAX_IN;
245	be_cont = be_cont -> cont, i -= MAX_IN) {}
246    if (i == MAX_IN) {
247	be_cont -> cont = new_back_edges();
248	be_cont = be_cont -> cont;
249	i = 0;
250    }
251    be_cont -> edges[i] = p;
252    be -> n_edges++;
253    if (be -> n_edges == 100) {
254#       if 0
255	  if (GC_print_stats) {
256	    GC_err_printf("The following object has in-degree >= 100:\n");
257	    GC_print_heap_obj(q);
258	  }
259#	endif
260    }
261}
262
263typedef void (*per_object_func)(ptr_t p, size_t n_bytes, word gc_descr);
264
265static void per_object_helper(struct hblk *h, word fn)
266{
267  hdr * hhdr = HDR(h);
268  size_t sz = hhdr -> hb_sz;
269  word descr = hhdr -> hb_descr;
270  per_object_func f = (per_object_func)fn;
271  int i = 0;
272
273  do {
274    f((ptr_t)(h -> hb_body + i), sz, descr);
275    i += sz;
276  } while (i + sz <= BYTES_TO_WORDS(HBLKSIZE));
277}
278
279void GC_apply_to_each_object(per_object_func f)
280{
281  GC_apply_to_all_blocks(per_object_helper, (word)f);
282}
283
284static void reset_back_edge(ptr_t p, size_t n_bytes, word gc_descr)
285{
286  /* Skip any free list links, or dropped blocks */
287  if (GC_HAS_DEBUG_INFO(p)) {
288    ptr_t old_back_ptr = GET_OH_BG_PTR(p);
289    if ((word)old_back_ptr & FLAG_MANY) {
290      back_edges *be = (back_edges *)((word)old_back_ptr & ~FLAG_MANY);
291      if (!(be -> flags & RETAIN)) {
292	deallocate_back_edges(be);
293        SET_OH_BG_PTR(p, 0); 
294      } else {
295        word *currentp;
296
297	GC_ASSERT(GC_is_marked(p));
298
299	/* Back edges may point to objects that will not be retained. 	*/
300	/* Delete them for now, but remember the height.		*/
301	/* Some will be added back at next GC.				*/
302	  be -> n_edges = 0;
303	  if (0 != be -> cont) {
304	    deallocate_back_edges(be -> cont);
305	    be -> cont = 0;
306	  }
307
308	GC_ASSERT(GC_is_marked(p));
309
310	/* We only retain things for one GC cycle at a time.		*/
311	  be -> flags &= ~RETAIN;
312      }
313    } else /* Simple back pointer */ {
314      /* Clear to avoid dangling pointer. */
315      SET_OH_BG_PTR(p, 0);
316    }
317  }
318}
319
320static void add_back_edges(ptr_t p, size_t n_bytes, word gc_descr)
321{
322  word *currentp = (word *)(p + sizeof(oh));
323
324  /* For now, fix up non-length descriptors conservatively.	*/
325    if((gc_descr & GC_DS_TAGS) != GC_DS_LENGTH) {
326      gc_descr = n_bytes;
327    }
328  while (currentp < (word *)(p + gc_descr)) {
329    word current = *currentp++;
330    FIXUP_POINTER(current);
331    if (current >= (word)GC_least_plausible_heap_addr && 
332	current <= (word)GC_greatest_plausible_heap_addr) {
333       ptr_t target = GC_base((void *)current);
334       if (0 != target) {
335	 add_edge(p, target);
336       }
337    }
338  }
339}
340
341/* Rebuild the representation of the backward reachability graph.	*/
342/* Does not examine mark bits.  Can be called before GC.		*/
343void GC_build_back_graph(void)
344{
345  GC_apply_to_each_object(add_back_edges);
346}
347
348/* Return an approximation to the length of the longest simple path	*/
349/* through unreachable objects to p.  We refer to this as the height	*/
350/* of p.								*/
351static word backwards_height(ptr_t p)
352{
353  word result;
354  ptr_t back_ptr = GET_OH_BG_PTR(p);
355  back_edges *be;
356
357  if (0 == back_ptr) return 1;
358  if (!((word)back_ptr & FLAG_MANY)) {
359    if (is_in_progress(p)) return 0;  /* DFS back edge, i.e. we followed  */
360    				      /* an edge to an object already	  */
361    				      /* on our stack: ignore		  */
362    push_in_progress(p);
363    result = backwards_height(back_ptr)+1;
364    pop_in_progress(p);
365    return result;
366  }
367  be = (back_edges *)((word)back_ptr & ~FLAG_MANY);
368  if (be -> height >= 0 && be -> height_gc_no == GC_gc_no)
369      return be -> height;
370  /* Ignore back edges in DFS */
371    if (be -> height == HEIGHT_IN_PROGRESS) return 0;
372  result = (be -> height > 0? be -> height : 1);
373  be -> height = HEIGHT_IN_PROGRESS;
374  FOR_EACH_PRED(q, p, {
375    word this_height;
376    if (GC_is_marked(q) && !(FLAG_MANY & (word)GET_OH_BG_PTR(p))) {
377      if (GC_print_stats)
378	  GC_log_printf("Found bogus pointer from 0x%lx to 0x%lx\n", q, p);
379	/* Reachable object "points to" unreachable one.		*/
380	/* Could be caused by our lax treatment of GC descriptors.	*/
381      this_height = 1;
382    } else {
383        this_height = backwards_height(q);
384    }
385    if (this_height >= result) result = this_height + 1;
386  });
387  be -> height = result;
388  be -> height_gc_no = GC_gc_no;
389  return result;
390}
391
392word GC_max_height;
393ptr_t GC_deepest_obj;
394
395/* Compute the maximum height of every unreachable predecessor p of  a 	*/
396/* reachable object.  Arrange to save the heights of all such objects p	*/
397/* so that they can be used in calculating the height of objects in the	*/
398/* next GC.								*/
399/* Set GC_max_height to be the maximum height we encounter, and 	*/
400/* GC_deepest_obj to be the corresponding object.			*/
401static void update_max_height(ptr_t p, size_t n_bytes, word gc_descr)
402{
403  if (GC_is_marked(p) && GC_HAS_DEBUG_INFO(p)) {
404    int i;
405    word p_height = 0;
406    ptr_t p_deepest_obj = 0;
407    ptr_t back_ptr;
408    back_edges *be = 0;
409
410    /* If we remembered a height last time, use it as a minimum.	*/
411    /* It may have increased due to newly unreachable chains pointing	*/
412    /* to p, but it can't have decreased.				*/
413    back_ptr = GET_OH_BG_PTR(p);
414    if (0 != back_ptr && ((word)back_ptr & FLAG_MANY)) {
415      be = (back_edges *)((word)back_ptr & ~FLAG_MANY);
416      if (be -> height != HEIGHT_UNKNOWN) p_height = be -> height;
417    }
418    FOR_EACH_PRED(q, p, {
419      if (!GC_is_marked(q) && GC_HAS_DEBUG_INFO(q)) {
420        word q_height;
421
422        q_height = backwards_height(q);
423	if (q_height > p_height) {
424	  p_height = q_height;
425	  p_deepest_obj = q;
426	}
427      }
428    });
429    if (p_height > 0) {
430      /* Remember the height for next time. */
431	if (be == 0) {
432    	  ensure_struct(p);
433    	  back_ptr = GET_OH_BG_PTR(p);
434  	  be = (back_edges *)((word)back_ptr & ~FLAG_MANY);
435	}
436	be -> flags |= RETAIN;
437	be -> height = p_height;
438	be -> height_gc_no = GC_gc_no;
439    }
440    if (p_height > GC_max_height) {
441	GC_max_height = p_height;
442	GC_deepest_obj = p_deepest_obj;
443    }
444  }
445}
446
447word GC_max_max_height = 0;
448
449void GC_traverse_back_graph(void)
450{
451  GC_max_height = 0;
452  GC_apply_to_each_object(update_max_height);
453  if (0 != GC_deepest_obj)
454    GC_set_mark_bit(GC_deepest_obj);  /* Keep it until we can print it. */
455}
456
457void GC_print_back_graph_stats(void)
458{
459  GC_printf("Maximum backwards height of reachable objects at GC %lu is %ld\n",
460	    (unsigned long) GC_gc_no, (unsigned long)GC_max_height);
461  if (GC_max_height > GC_max_max_height) {
462    GC_max_max_height = GC_max_height;
463    GC_printf("The following unreachable object is last in a longest chain "
464	      "of unreachable objects:\n");
465    GC_print_heap_obj(GC_deepest_obj);
466  }
467  if (GC_print_stats) {
468    GC_log_printf("Needed max total of %ld back-edge structs\n",
469	          GC_n_back_edge_structs);
470  }
471  GC_apply_to_each_object(reset_back_edge);
472  GC_deepest_obj = 0;
473}
474
475#endif /* MAKE_BACK_GRAPH */