]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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 "private/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 10000 | |
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 collector. */ | |
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(struct hblk *h) | |
54 | struct hblk *h; | |
55 | { | |
56 | hdr * hhdr = HDR(h); | |
57 | int sz = BYTES_TO_WORDS(hhdr -> hb_sz); | |
58 | ptr_t p; | |
59 | ||
60 | if (sz > MAXOBJWORDS) 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(struct hblk *h, int index) | |
74 | { | |
75 | page_entry *pe = GC_sums + index; | |
76 | register hdr * hhdr = HDR(h); | |
77 | struct hblk *b; | |
78 | ||
79 | if (pe -> block != 0 && pe -> block != h + OFFSET) ABORT("goofed"); | |
80 | pe -> old_sum = pe -> new_sum; | |
81 | pe -> new_sum = GC_checksum(h); | |
82 | # if !defined(MSWIN32) && !defined(MSWINCE) | |
83 | if (pe -> new_sum != 0x80000000 && !GC_page_was_ever_dirty(h)) { | |
84 | GC_printf("GC_page_was_ever_dirty(%p) is wrong\n", h); | |
85 | } | |
86 | # endif | |
87 | if (GC_page_was_dirty(h)) { | |
88 | GC_n_dirty++; | |
89 | } else { | |
90 | GC_n_clean++; | |
91 | } | |
92 | b = h; | |
93 | while (IS_FORWARDING_ADDR_OR_NIL(hhdr) && hhdr != 0) { | |
94 | b -= (word)hhdr; | |
95 | hhdr = HDR(b); | |
96 | } | |
97 | if (pe -> new_valid | |
98 | && hhdr != 0 && hhdr -> hb_descr != 0 /* may contain pointers */ | |
99 | && pe -> old_sum != pe -> new_sum) { | |
100 | if (!GC_page_was_dirty(h) || !GC_page_was_ever_dirty(h)) { | |
101 | /* Set breakpoint here */GC_n_dirty_errors++; | |
102 | } | |
103 | # ifdef STUBBORN_ALLOC | |
104 | if (!HBLK_IS_FREE(hhdr) | |
105 | && hhdr -> hb_obj_kind == STUBBORN | |
106 | && !GC_page_was_changed(h) | |
107 | && !GC_on_free_list(h)) { | |
108 | /* if GC_on_free_list(h) then reclaim may have touched it */ | |
109 | /* without any allocations taking place. */ | |
110 | /* Set breakpoint here */GC_n_changed_errors++; | |
111 | } | |
112 | # endif | |
113 | } | |
114 | pe -> new_valid = TRUE; | |
115 | pe -> block = h + OFFSET; | |
116 | } | |
117 | ||
118 | unsigned long GC_bytes_in_used_blocks; | |
119 | ||
120 | void GC_add_block(h, dummy) | |
121 | struct hblk *h; | |
122 | word dummy; | |
123 | { | |
124 | hdr * hhdr = HDR(h); | |
125 | bytes = hhdr -> hb_sz; | |
126 | ||
127 | bytes += HBLKSIZE-1; | |
128 | bytes &= ~(HBLKSIZE-1); | |
129 | GC_bytes_in_used_blocks += bytes; | |
130 | } | |
131 | ||
132 | void GC_check_blocks() | |
133 | { | |
134 | unsigned long bytes_in_free_blocks = GC_large_free_bytes; | |
135 | ||
136 | GC_bytes_in_used_blocks = 0; | |
137 | GC_apply_to_all_blocks(GC_add_block, (word)0); | |
138 | GC_printf("GC_bytes_in_used_blocks = %lu, bytes_in_free_blocks = %lu ", | |
139 | GC_bytes_in_used_blocks, bytes_in_free_blocks); | |
140 | GC_printf("GC_heapsize = %lu\n", (unsigned long)GC_heapsize); | |
141 | if (GC_bytes_in_used_blocks + bytes_in_free_blocks != GC_heapsize) { | |
142 | GC_printf("LOST SOME BLOCKS!!\n"); | |
143 | } | |
144 | } | |
145 | ||
146 | /* Should be called immediately after GC_read_dirty and GC_read_changed. */ | |
147 | void GC_check_dirty() | |
148 | { | |
149 | register int index; | |
150 | register unsigned i; | |
151 | register struct hblk *h; | |
152 | register ptr_t start; | |
153 | ||
154 | GC_check_blocks(); | |
155 | ||
156 | GC_n_dirty_errors = 0; | |
157 | GC_n_changed_errors = 0; | |
158 | GC_n_clean = 0; | |
159 | GC_n_dirty = 0; | |
160 | ||
161 | index = 0; | |
162 | for (i = 0; i < GC_n_heap_sects; i++) { | |
163 | start = GC_heap_sects[i].hs_start; | |
164 | for (h = (struct hblk *)start; | |
165 | h < (struct hblk *)(start + GC_heap_sects[i].hs_bytes); | |
166 | h++) { | |
167 | GC_update_check_page(h, index); | |
168 | index++; | |
169 | if (index >= NSUMS) goto out; | |
170 | } | |
171 | } | |
172 | out: | |
173 | GC_printf("Checked %lu clean and %lu dirty pages\n", | |
174 | (unsigned long) GC_n_clean, (unsigned long) GC_n_dirty); | |
175 | if (GC_n_dirty_errors > 0) { | |
176 | GC_printf("Found %lu dirty bit errors\n", | |
177 | (unsigned long)GC_n_dirty_errors); | |
178 | } | |
179 | if (GC_n_changed_errors > 0) { | |
180 | GC_printf("Found %lu changed bit errors\n", | |
181 | (unsigned long)GC_n_changed_errors); | |
182 | GC_printf("These may be benign (provoked by nonpointer changes)\n"); | |
183 | # ifdef THREADS | |
184 | GC_printf( | |
185 | "Also expect 1 per thread currently allocating a stubborn obj.\n"); | |
186 | # endif | |
187 | } | |
188 | } | |
189 | ||
190 | # else | |
191 | ||
192 | extern int GC_quiet; | |
193 | /* ANSI C doesn't allow translation units to be empty. */ | |
194 | /* So we guarantee this one is nonempty. */ | |
195 | ||
196 | # endif /* CHECKSUMS */ |