| 1 | /*
|
|---|
| 2 | * Copyright (c) 1992-1994 by Xerox Corporation. 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 | /* Boehm, March 29, 1995 12:51 pm PST */
|
|---|
| 14 | # ifdef CHECKSUMS
|
|---|
| 15 |
|
|---|
| 16 | # include "gc_priv.h"
|
|---|
| 17 |
|
|---|
| 18 | /* This is debugging code intended to verify the results of dirty bit */
|
|---|
| 19 | /* computations. Works only in a single threaded environment. */
|
|---|
| 20 | /* We assume that stubborn objects are changed only when they are */
|
|---|
| 21 | /* enabled for writing. (Certain kinds of writing are actually */
|
|---|
| 22 | /* safe under other conditions.) */
|
|---|
| 23 | # define NSUMS 2000
|
|---|
| 24 |
|
|---|
| 25 | # define OFFSET 0x10000
|
|---|
| 26 |
|
|---|
| 27 | typedef struct {
|
|---|
| 28 | GC_bool new_valid;
|
|---|
| 29 | word old_sum;
|
|---|
| 30 | word new_sum;
|
|---|
| 31 | struct hblk * block; /* Block to which this refers + OFFSET */
|
|---|
| 32 | /* to hide it from colector. */
|
|---|
| 33 | } page_entry;
|
|---|
| 34 |
|
|---|
| 35 | page_entry GC_sums [NSUMS];
|
|---|
| 36 |
|
|---|
| 37 | word GC_checksum(h)
|
|---|
| 38 | struct hblk *h;
|
|---|
| 39 | {
|
|---|
| 40 | register word *p = (word *)h;
|
|---|
| 41 | register word *lim = (word *)(h+1);
|
|---|
| 42 | register word result = 0;
|
|---|
| 43 |
|
|---|
| 44 | while (p < lim) {
|
|---|
| 45 | result += *p++;
|
|---|
| 46 | }
|
|---|
| 47 | return(result | 0x80000000 /* doesn't look like pointer */);
|
|---|
| 48 | }
|
|---|
| 49 |
|
|---|
| 50 | # ifdef STUBBORN_ALLOC
|
|---|
| 51 | /* Check whether a stubborn object from the given block appears on */
|
|---|
| 52 | /* the appropriate free list. */
|
|---|
| 53 | GC_bool GC_on_free_list(h)
|
|---|
| 54 | struct hblk *h;
|
|---|
| 55 | {
|
|---|
| 56 | register hdr * hhdr = HDR(h);
|
|---|
| 57 | register int sz = hhdr -> hb_sz;
|
|---|
| 58 | ptr_t p;
|
|---|
| 59 |
|
|---|
| 60 | if (sz > MAXOBJSZ) return(FALSE);
|
|---|
| 61 | for (p = GC_sobjfreelist[sz]; p != 0; p = obj_link(p)) {
|
|---|
| 62 | if (HBLKPTR(p) == h) return(TRUE);
|
|---|
| 63 | }
|
|---|
| 64 | return(FALSE);
|
|---|
| 65 | }
|
|---|
| 66 | # endif
|
|---|
| 67 |
|
|---|
| 68 | int GC_n_dirty_errors;
|
|---|
| 69 | int GC_n_changed_errors;
|
|---|
| 70 | int GC_n_clean;
|
|---|
| 71 | int GC_n_dirty;
|
|---|
| 72 |
|
|---|
| 73 | void GC_update_check_page(h, index)
|
|---|
| 74 | struct hblk *h;
|
|---|
| 75 | int index;
|
|---|
| 76 | {
|
|---|
| 77 | page_entry *pe = GC_sums + index;
|
|---|
| 78 | register hdr * hhdr = HDR(h);
|
|---|
| 79 |
|
|---|
| 80 | if (pe -> block != 0 && pe -> block != h + OFFSET) ABORT("goofed");
|
|---|
| 81 | pe -> old_sum = pe -> new_sum;
|
|---|
| 82 | pe -> new_sum = GC_checksum(h);
|
|---|
| 83 | # if !defined(MSWIN32) && !defined(MSWINCE)
|
|---|
| 84 | if (pe -> new_sum != 0 && !GC_page_was_ever_dirty(h)) {
|
|---|
| 85 | GC_printf1("GC_page_was_ever_dirty(0x%lx) is wrong\n",
|
|---|
| 86 | (unsigned long)h);
|
|---|
| 87 | }
|
|---|
| 88 | # endif
|
|---|
| 89 | if (GC_page_was_dirty(h)) {
|
|---|
| 90 | GC_n_dirty++;
|
|---|
| 91 | } else {
|
|---|
| 92 | GC_n_clean++;
|
|---|
| 93 | }
|
|---|
| 94 | if (pe -> new_valid && pe -> old_sum != pe -> new_sum) {
|
|---|
| 95 | if (!GC_page_was_dirty(h) || !GC_page_was_ever_dirty(h)) {
|
|---|
| 96 | /* Set breakpoint here */GC_n_dirty_errors++;
|
|---|
| 97 | }
|
|---|
| 98 | # ifdef STUBBORN_ALLOC
|
|---|
| 99 | if (!IS_FORWARDING_ADDR_OR_NIL(hhdr)
|
|---|
| 100 | && hhdr -> hb_map != GC_invalid_map
|
|---|
| 101 | && hhdr -> hb_obj_kind == STUBBORN
|
|---|
| 102 | && !GC_page_was_changed(h)
|
|---|
| 103 | && !GC_on_free_list(h)) {
|
|---|
| 104 | /* if GC_on_free_list(h) then reclaim may have touched it */
|
|---|
| 105 | /* without any allocations taking place. */
|
|---|
| 106 | /* Set breakpoint here */GC_n_changed_errors++;
|
|---|
| 107 | }
|
|---|
| 108 | # endif
|
|---|
| 109 | }
|
|---|
| 110 | pe -> new_valid = TRUE;
|
|---|
| 111 | pe -> block = h + OFFSET;
|
|---|
| 112 | }
|
|---|
| 113 |
|
|---|
| 114 | word GC_bytes_in_used_blocks;
|
|---|
| 115 |
|
|---|
| 116 | void GC_add_block(h, dummy)
|
|---|
| 117 | struct hblk *h;
|
|---|
| 118 | word dummy;
|
|---|
| 119 | {
|
|---|
| 120 | register hdr * hhdr = HDR(h);
|
|---|
| 121 | register bytes = WORDS_TO_BYTES(hhdr -> hb_sz);
|
|---|
| 122 |
|
|---|
| 123 | bytes += HDR_BYTES + HBLKSIZE-1;
|
|---|
| 124 | bytes &= ~(HBLKSIZE-1);
|
|---|
| 125 | GC_bytes_in_used_blocks += bytes;
|
|---|
| 126 | }
|
|---|
| 127 |
|
|---|
| 128 | void GC_check_blocks()
|
|---|
| 129 | {
|
|---|
| 130 | word bytes_in_free_blocks = 0;
|
|---|
| 131 | struct hblk * h = GC_hblkfreelist;
|
|---|
| 132 | hdr * hhdr = HDR(h);
|
|---|
| 133 | word sz;
|
|---|
| 134 |
|
|---|
| 135 | GC_bytes_in_used_blocks = 0;
|
|---|
| 136 | GC_apply_to_all_blocks(GC_add_block, (word)0);
|
|---|
| 137 | while (h != 0) {
|
|---|
| 138 | sz = hhdr -> hb_sz;
|
|---|
| 139 | bytes_in_free_blocks += sz;
|
|---|
| 140 | h = hhdr -> hb_next;
|
|---|
| 141 | hhdr = HDR(h);
|
|---|
| 142 | }
|
|---|
| 143 | GC_printf2("GC_bytes_in_used_blocks = %ld, bytes_in_free_blocks = %ld ",
|
|---|
| 144 | GC_bytes_in_used_blocks, bytes_in_free_blocks);
|
|---|
| 145 | GC_printf1("GC_heapsize = %ld\n", GC_heapsize);
|
|---|
| 146 | if (GC_bytes_in_used_blocks + bytes_in_free_blocks != GC_heapsize) {
|
|---|
| 147 | GC_printf0("LOST SOME BLOCKS!!\n");
|
|---|
| 148 | }
|
|---|
| 149 | }
|
|---|
| 150 |
|
|---|
| 151 | /* Should be called immediately after GC_read_dirty and GC_read_changed. */
|
|---|
| 152 | void GC_check_dirty()
|
|---|
| 153 | {
|
|---|
| 154 | register int index;
|
|---|
| 155 | register unsigned i;
|
|---|
| 156 | register struct hblk *h;
|
|---|
| 157 | register ptr_t start;
|
|---|
| 158 |
|
|---|
| 159 | GC_check_blocks();
|
|---|
| 160 |
|
|---|
| 161 | GC_n_dirty_errors = 0;
|
|---|
| 162 | GC_n_changed_errors = 0;
|
|---|
| 163 | GC_n_clean = 0;
|
|---|
| 164 | GC_n_dirty = 0;
|
|---|
| 165 |
|
|---|
| 166 | index = 0;
|
|---|
| 167 | for (i = 0; i < GC_n_heap_sects; i++) {
|
|---|
| 168 | start = GC_heap_sects[i].hs_start;
|
|---|
| 169 | for (h = (struct hblk *)start;
|
|---|
| 170 | h < (struct hblk *)(start + GC_heap_sects[i].hs_bytes);
|
|---|
| 171 | h++) {
|
|---|
| 172 | GC_update_check_page(h, index);
|
|---|
| 173 | index++;
|
|---|
| 174 | if (index >= NSUMS) goto out;
|
|---|
| 175 | }
|
|---|
| 176 | }
|
|---|
| 177 | out:
|
|---|
| 178 | GC_printf2("Checked %lu clean and %lu dirty pages\n",
|
|---|
| 179 | (unsigned long) GC_n_clean, (unsigned long) GC_n_dirty);
|
|---|
| 180 | if (GC_n_dirty_errors > 0) {
|
|---|
| 181 | GC_printf1("Found %lu dirty bit errors\n",
|
|---|
| 182 | (unsigned long)GC_n_dirty_errors);
|
|---|
| 183 | }
|
|---|
| 184 | if (GC_n_changed_errors > 0) {
|
|---|
| 185 | GC_printf1("Found %lu changed bit errors\n",
|
|---|
| 186 | (unsigned long)GC_n_changed_errors);
|
|---|
| 187 | GC_printf0("These may be benign (provoked by nonpointer changes)\n");
|
|---|
| 188 | # ifdef THREADS
|
|---|
| 189 | GC_printf0(
|
|---|
| 190 | "Also expect 1 per thread currently allocating a stubborn obj.\n");
|
|---|
| 191 | # endif
|
|---|
| 192 | }
|
|---|
| 193 | }
|
|---|
| 194 |
|
|---|
| 195 | # else
|
|---|
| 196 |
|
|---|
| 197 | extern int GC_quiet;
|
|---|
| 198 | /* ANSI C doesn't allow translation units to be empty. */
|
|---|
| 199 | /* So we guarantee this one is nonempty. */
|
|---|
| 200 |
|
|---|
| 201 | # endif /* CHECKSUMS */
|
|---|