Mercurial > hg > truffle
annotate src/share/vm/classfile/loaderConstraints.cpp @ 2137:ffd725ff6943
Merge
author | johnc |
---|---|
date | Thu, 13 Jan 2011 17:19:21 -0800 |
parents | f95d63e2154a |
children | 3582bf76420e |
rev | line source |
---|---|
0 | 1 /* |
1972 | 2 * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. |
0 | 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 * | |
5 * This code is free software; you can redistribute it and/or modify it | |
6 * under the terms of the GNU General Public License version 2 only, as | |
7 * published by the Free Software Foundation. | |
8 * | |
9 * This code is distributed in the hope that it will be useful, but WITHOUT | |
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
12 * version 2 for more details (a copy is included in the LICENSE file that | |
13 * accompanied this code). | |
14 * | |
15 * You should have received a copy of the GNU General Public License version | |
16 * 2 along with this work; if not, write to the Free Software Foundation, | |
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
18 * | |
1552
c18cbe5936b8
6941466: Oracle rebranding changes for Hotspot repositories
trims
parents:
1489
diff
changeset
|
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
c18cbe5936b8
6941466: Oracle rebranding changes for Hotspot repositories
trims
parents:
1489
diff
changeset
|
20 * or visit www.oracle.com if you need additional information or have any |
c18cbe5936b8
6941466: Oracle rebranding changes for Hotspot repositories
trims
parents:
1489
diff
changeset
|
21 * questions. |
0 | 22 * |
23 */ | |
24 | |
1972 | 25 #include "precompiled.hpp" |
26 #include "classfile/loaderConstraints.hpp" | |
27 #include "memory/resourceArea.hpp" | |
28 #include "oops/oop.inline.hpp" | |
29 #include "runtime/handles.inline.hpp" | |
30 #include "runtime/safepoint.hpp" | |
31 #include "utilities/hashtable.inline.hpp" | |
0 | 32 |
33 LoaderConstraintTable::LoaderConstraintTable(int nof_buckets) | |
34 : Hashtable(nof_buckets, sizeof(LoaderConstraintEntry)) {}; | |
35 | |
36 | |
37 LoaderConstraintEntry* LoaderConstraintTable::new_entry( | |
38 unsigned int hash, symbolOop name, | |
39 klassOop klass, int num_loaders, | |
40 int max_loaders) { | |
41 LoaderConstraintEntry* entry; | |
42 entry = (LoaderConstraintEntry*)Hashtable::new_entry(hash, klass); | |
43 entry->set_name(name); | |
44 entry->set_num_loaders(num_loaders); | |
45 entry->set_max_loaders(max_loaders); | |
46 return entry; | |
47 } | |
48 | |
49 | |
50 void LoaderConstraintTable::oops_do(OopClosure* f) { | |
51 for (int index = 0; index < table_size(); index++) { | |
52 for (LoaderConstraintEntry* probe = bucket(index); | |
53 probe != NULL; | |
54 probe = probe->next()) { | |
55 f->do_oop((oop*)(probe->name_addr())); | |
56 if (probe->klass() != NULL) { | |
57 f->do_oop((oop*)probe->klass_addr()); | |
58 } | |
59 for (int n = 0; n < probe->num_loaders(); n++) { | |
60 if (probe->loader(n) != NULL) { | |
61 f->do_oop(probe->loader_addr(n)); | |
62 } | |
63 } | |
64 } | |
65 } | |
66 } | |
67 | |
68 // We must keep the symbolOop used in the name alive. We'll use the | |
69 // loaders to decide if a particular entry can be purged. | |
70 void LoaderConstraintTable::always_strong_classes_do(OopClosure* blk) { | |
71 // We must keep the symbolOop used in the name alive. | |
72 for (int cindex = 0; cindex < table_size(); cindex++) { | |
73 for (LoaderConstraintEntry* lc_probe = bucket(cindex); | |
74 lc_probe != NULL; | |
75 lc_probe = lc_probe->next()) { | |
76 assert (lc_probe->name() != NULL, "corrupted loader constraint table"); | |
77 blk->do_oop((oop*)lc_probe->name_addr()); | |
78 } | |
79 } | |
80 } | |
81 | |
82 | |
83 // The loaderConstraintTable must always be accessed with the | |
84 // SystemDictionary lock held. This is true even for readers as | |
85 // entries in the table could be being dynamically resized. | |
86 | |
87 LoaderConstraintEntry** LoaderConstraintTable::find_loader_constraint( | |
88 symbolHandle name, Handle loader) { | |
89 | |
90 unsigned int hash = compute_hash(name); | |
91 int index = hash_to_index(hash); | |
92 LoaderConstraintEntry** pp = bucket_addr(index); | |
93 while (*pp) { | |
94 LoaderConstraintEntry* p = *pp; | |
95 if (p->hash() == hash) { | |
96 if (p->name() == name()) { | |
97 for (int i = p->num_loaders() - 1; i >= 0; i--) { | |
98 if (p->loader(i) == loader()) { | |
99 return pp; | |
100 } | |
101 } | |
102 } | |
103 } | |
104 pp = p->next_addr(); | |
105 } | |
106 return pp; | |
107 } | |
108 | |
109 | |
110 void LoaderConstraintTable::purge_loader_constraints(BoolObjectClosure* is_alive) { | |
1489
cff162798819
6888953: some calls to function-like macros are missing semicolons
jcoomes
parents:
1339
diff
changeset
|
111 assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint"); |
0 | 112 // Remove unloaded entries from constraint table |
113 for (int index = 0; index < table_size(); index++) { | |
114 LoaderConstraintEntry** p = bucket_addr(index); | |
115 while(*p) { | |
116 LoaderConstraintEntry* probe = *p; | |
117 klassOop klass = probe->klass(); | |
118 // Remove klass that is no longer alive | |
119 if (klass != NULL && !is_alive->do_object_b(klass)) { | |
120 probe->set_klass(NULL); | |
121 if (TraceLoaderConstraints) { | |
122 ResourceMark rm; | |
123 tty->print_cr("[Purging class object from constraint for name %s," | |
124 " loader list:", | |
125 probe->name()->as_C_string()); | |
126 for (int i = 0; i < probe->num_loaders(); i++) { | |
127 tty->print_cr("[ [%d]: %s", i, | |
128 SystemDictionary::loader_name(probe->loader(i))); | |
129 } | |
130 } | |
131 } | |
132 // Remove entries no longer alive from loader array | |
133 int n = 0; | |
134 while (n < probe->num_loaders()) { | |
135 if (probe->loader(n) != NULL) { | |
136 if (!is_alive->do_object_b(probe->loader(n))) { | |
137 if (TraceLoaderConstraints) { | |
138 ResourceMark rm; | |
139 tty->print_cr("[Purging loader %s from constraint for name %s", | |
140 SystemDictionary::loader_name(probe->loader(n)), | |
141 probe->name()->as_C_string() | |
142 ); | |
143 } | |
144 | |
145 // Compact array | |
146 int num = probe->num_loaders() - 1; | |
147 probe->set_num_loaders(num); | |
148 probe->set_loader(n, probe->loader(num)); | |
149 probe->set_loader(num, NULL); | |
150 | |
151 if (TraceLoaderConstraints) { | |
152 ResourceMark rm; | |
153 tty->print_cr("[New loader list:"); | |
154 for (int i = 0; i < probe->num_loaders(); i++) { | |
155 tty->print_cr("[ [%d]: %s", i, | |
156 SystemDictionary::loader_name(probe->loader(i))); | |
157 } | |
158 } | |
159 | |
160 continue; // current element replaced, so restart without | |
161 // incrementing n | |
162 } | |
163 } | |
164 n++; | |
165 } | |
166 // Check whether entry should be purged | |
167 if (probe->num_loaders() < 2) { | |
168 if (TraceLoaderConstraints) { | |
169 ResourceMark rm; | |
170 tty->print("[Purging complete constraint for name %s\n", | |
171 probe->name()->as_C_string()); | |
172 } | |
173 | |
174 // Purge entry | |
175 *p = probe->next(); | |
176 FREE_C_HEAP_ARRAY(oop, probe->loaders()); | |
177 free_entry(probe); | |
178 } else { | |
179 #ifdef ASSERT | |
180 assert(is_alive->do_object_b(probe->name()), "name should be live"); | |
181 if (probe->klass() != NULL) { | |
182 assert(is_alive->do_object_b(probe->klass()), "klass should be live"); | |
183 } | |
184 for (n = 0; n < probe->num_loaders(); n++) { | |
185 if (probe->loader(n) != NULL) { | |
186 assert(is_alive->do_object_b(probe->loader(n)), "loader should be live"); | |
187 } | |
188 } | |
189 #endif | |
190 // Go to next entry | |
191 p = probe->next_addr(); | |
192 } | |
193 } | |
194 } | |
195 } | |
196 | |
197 bool LoaderConstraintTable::add_entry(symbolHandle class_name, | |
198 klassOop klass1, Handle class_loader1, | |
199 klassOop klass2, Handle class_loader2) { | |
200 int failure_code = 0; // encode different reasons for failing | |
201 | |
202 if (klass1 != NULL && klass2 != NULL && klass1 != klass2) { | |
203 failure_code = 1; | |
204 } else { | |
205 klassOop klass = klass1 != NULL ? klass1 : klass2; | |
206 | |
207 LoaderConstraintEntry** pp1 = find_loader_constraint(class_name, | |
208 class_loader1); | |
209 if (*pp1 != NULL && (*pp1)->klass() != NULL) { | |
210 if (klass != NULL) { | |
211 if (klass != (*pp1)->klass()) { | |
212 failure_code = 2; | |
213 } | |
214 } else { | |
215 klass = (*pp1)->klass(); | |
216 } | |
217 } | |
218 | |
219 LoaderConstraintEntry** pp2 = find_loader_constraint(class_name, | |
220 class_loader2); | |
221 if (*pp2 != NULL && (*pp2)->klass() != NULL) { | |
222 if (klass != NULL) { | |
223 if (klass != (*pp2)->klass()) { | |
224 failure_code = 3; | |
225 } | |
226 } else { | |
227 klass = (*pp2)->klass(); | |
228 } | |
229 } | |
230 | |
231 if (failure_code == 0) { | |
232 if (*pp1 == NULL && *pp2 == NULL) { | |
233 unsigned int hash = compute_hash(class_name); | |
234 int index = hash_to_index(hash); | |
235 LoaderConstraintEntry* p; | |
236 p = new_entry(hash, class_name(), klass, 2, 2); | |
237 p->set_loaders(NEW_C_HEAP_ARRAY(oop, 2)); | |
238 p->set_loader(0, class_loader1()); | |
239 p->set_loader(1, class_loader2()); | |
240 p->set_klass(klass); | |
241 p->set_next(bucket(index)); | |
242 set_entry(index, p); | |
243 if (TraceLoaderConstraints) { | |
244 ResourceMark rm; | |
245 tty->print("[Adding new constraint for name: %s, loader[0]: %s," | |
246 " loader[1]: %s ]\n", | |
247 class_name()->as_C_string(), | |
248 SystemDictionary::loader_name(class_loader1()), | |
249 SystemDictionary::loader_name(class_loader2()) | |
250 ); | |
251 } | |
252 } else if (*pp1 == *pp2) { | |
253 /* constraint already imposed */ | |
254 if ((*pp1)->klass() == NULL) { | |
255 (*pp1)->set_klass(klass); | |
256 if (TraceLoaderConstraints) { | |
257 ResourceMark rm; | |
258 tty->print("[Setting class object in existing constraint for" | |
259 " name: %s and loader %s ]\n", | |
260 class_name()->as_C_string(), | |
261 SystemDictionary::loader_name(class_loader1()) | |
262 ); | |
263 } | |
264 } else { | |
265 assert((*pp1)->klass() == klass, "loader constraints corrupted"); | |
266 } | |
267 } else if (*pp1 == NULL) { | |
268 extend_loader_constraint(*pp2, class_loader1, klass); | |
269 } else if (*pp2 == NULL) { | |
270 extend_loader_constraint(*pp1, class_loader2, klass); | |
271 } else { | |
272 merge_loader_constraints(pp1, pp2, klass); | |
273 } | |
274 } | |
275 } | |
276 | |
277 if (failure_code != 0 && TraceLoaderConstraints) { | |
278 ResourceMark rm; | |
279 const char* reason = ""; | |
280 switch(failure_code) { | |
281 case 1: reason = "the class objects presented by loader[0] and loader[1]" | |
282 " are different"; break; | |
283 case 2: reason = "the class object presented by loader[0] does not match" | |
284 " the stored class object in the constraint"; break; | |
285 case 3: reason = "the class object presented by loader[1] does not match" | |
286 " the stored class object in the constraint"; break; | |
287 default: reason = "unknown reason code"; | |
288 } | |
289 tty->print("[Failed to add constraint for name: %s, loader[0]: %s," | |
290 " loader[1]: %s, Reason: %s ]\n", | |
291 class_name()->as_C_string(), | |
292 SystemDictionary::loader_name(class_loader1()), | |
293 SystemDictionary::loader_name(class_loader2()), | |
294 reason | |
295 ); | |
296 } | |
297 | |
298 return failure_code == 0; | |
299 } | |
300 | |
301 | |
302 // return true if the constraint was updated, false if the constraint is | |
303 // violated | |
304 bool LoaderConstraintTable::check_or_update(instanceKlassHandle k, | |
305 Handle loader, | |
306 symbolHandle name) { | |
307 LoaderConstraintEntry* p = *(find_loader_constraint(name, loader)); | |
308 if (p && p->klass() != NULL && p->klass() != k()) { | |
309 if (TraceLoaderConstraints) { | |
310 ResourceMark rm; | |
311 tty->print("[Constraint check failed for name %s, loader %s: " | |
312 "the presented class object differs from that stored ]\n", | |
313 name()->as_C_string(), | |
314 SystemDictionary::loader_name(loader())); | |
315 } | |
316 return false; | |
317 } else { | |
318 if (p && p->klass() == NULL) { | |
319 p->set_klass(k()); | |
320 if (TraceLoaderConstraints) { | |
321 ResourceMark rm; | |
322 tty->print("[Updating constraint for name %s, loader %s, " | |
323 "by setting class object ]\n", | |
324 name()->as_C_string(), | |
325 SystemDictionary::loader_name(loader())); | |
326 } | |
327 } | |
328 return true; | |
329 } | |
330 } | |
331 | |
332 klassOop LoaderConstraintTable::find_constrained_klass(symbolHandle name, | |
333 Handle loader) { | |
334 LoaderConstraintEntry *p = *(find_loader_constraint(name, loader)); | |
335 if (p != NULL && p->klass() != NULL) | |
336 return p->klass(); | |
337 | |
338 // No constraints, or else no klass loaded yet. | |
339 return NULL; | |
340 } | |
341 | |
342 void LoaderConstraintTable::ensure_loader_constraint_capacity( | |
343 LoaderConstraintEntry *p, | |
344 int nfree) { | |
345 if (p->max_loaders() - p->num_loaders() < nfree) { | |
346 int n = nfree + p->num_loaders(); | |
347 oop* new_loaders = NEW_C_HEAP_ARRAY(oop, n); | |
348 memcpy(new_loaders, p->loaders(), sizeof(oop) * p->num_loaders()); | |
349 p->set_max_loaders(n); | |
350 FREE_C_HEAP_ARRAY(oop, p->loaders()); | |
351 p->set_loaders(new_loaders); | |
352 } | |
353 } | |
354 | |
355 | |
356 void LoaderConstraintTable::extend_loader_constraint(LoaderConstraintEntry* p, | |
357 Handle loader, | |
358 klassOop klass) { | |
359 ensure_loader_constraint_capacity(p, 1); | |
360 int num = p->num_loaders(); | |
361 p->set_loader(num, loader()); | |
362 p->set_num_loaders(num + 1); | |
363 if (TraceLoaderConstraints) { | |
364 ResourceMark rm; | |
365 tty->print("[Extending constraint for name %s by adding loader[%d]: %s %s", | |
366 p->name()->as_C_string(), | |
367 num, | |
368 SystemDictionary::loader_name(loader()), | |
369 (p->klass() == NULL ? " and setting class object ]\n" : " ]\n") | |
370 ); | |
371 } | |
372 if (p->klass() == NULL) { | |
373 p->set_klass(klass); | |
374 } else { | |
375 assert(klass == NULL || p->klass() == klass, "constraints corrupted"); | |
376 } | |
377 } | |
378 | |
379 | |
380 void LoaderConstraintTable::merge_loader_constraints( | |
381 LoaderConstraintEntry** pp1, | |
382 LoaderConstraintEntry** pp2, | |
383 klassOop klass) { | |
384 // make sure *pp1 has higher capacity | |
385 if ((*pp1)->max_loaders() < (*pp2)->max_loaders()) { | |
386 LoaderConstraintEntry** tmp = pp2; | |
387 pp2 = pp1; | |
388 pp1 = tmp; | |
389 } | |
390 | |
391 LoaderConstraintEntry* p1 = *pp1; | |
392 LoaderConstraintEntry* p2 = *pp2; | |
393 | |
394 ensure_loader_constraint_capacity(p1, p2->num_loaders()); | |
395 | |
396 for (int i = 0; i < p2->num_loaders(); i++) { | |
397 int num = p1->num_loaders(); | |
398 p1->set_loader(num, p2->loader(i)); | |
399 p1->set_num_loaders(num + 1); | |
400 } | |
401 | |
402 if (TraceLoaderConstraints) { | |
403 ResourceMark rm; | |
404 tty->print_cr("[Merged constraints for name %s, new loader list:", | |
405 p1->name()->as_C_string() | |
406 ); | |
407 | |
408 for (int i = 0; i < p1->num_loaders(); i++) { | |
409 tty->print_cr("[ [%d]: %s", i, | |
410 SystemDictionary::loader_name(p1->loader(i))); | |
411 } | |
412 if (p1->klass() == NULL) { | |
413 tty->print_cr("[... and setting class object]"); | |
414 } | |
415 } | |
416 | |
417 // p1->klass() will hold NULL if klass, p2->klass(), and old | |
418 // p1->klass() are all NULL. In addition, all three must have | |
419 // matching non-NULL values, otherwise either the constraints would | |
420 // have been violated, or the constraints had been corrupted (and an | |
421 // assertion would fail). | |
422 if (p2->klass() != NULL) { | |
423 assert(p2->klass() == klass, "constraints corrupted"); | |
424 } | |
425 if (p1->klass() == NULL) { | |
426 p1->set_klass(klass); | |
427 } else { | |
428 assert(p1->klass() == klass, "constraints corrupted"); | |
429 } | |
430 | |
431 *pp2 = p2->next(); | |
432 FREE_C_HEAP_ARRAY(oop, p2->loaders()); | |
433 free_entry(p2); | |
434 return; | |
435 } | |
436 | |
437 | |
1258
38836cf1d8d2
6920977: G1: guarantee(k == probe->klass(),"klass should be in dictionary") fails
tonyp
parents:
0
diff
changeset
|
438 void LoaderConstraintTable::verify(Dictionary* dictionary, |
38836cf1d8d2
6920977: G1: guarantee(k == probe->klass(),"klass should be in dictionary") fails
tonyp
parents:
0
diff
changeset
|
439 PlaceholderTable* placeholders) { |
0 | 440 Thread *thread = Thread::current(); |
441 for (int cindex = 0; cindex < _loader_constraint_size; cindex++) { | |
442 for (LoaderConstraintEntry* probe = bucket(cindex); | |
443 probe != NULL; | |
444 probe = probe->next()) { | |
445 guarantee(probe->name()->is_symbol(), "should be symbol"); | |
446 if (probe->klass() != NULL) { | |
447 instanceKlass* ik = instanceKlass::cast(probe->klass()); | |
448 guarantee(ik->name() == probe->name(), "name should match"); | |
449 symbolHandle name (thread, ik->name()); | |
450 Handle loader(thread, ik->class_loader()); | |
451 unsigned int d_hash = dictionary->compute_hash(name, loader); | |
452 int d_index = dictionary->hash_to_index(d_hash); | |
453 klassOop k = dictionary->find_class(d_index, d_hash, name, loader); | |
1258
38836cf1d8d2
6920977: G1: guarantee(k == probe->klass(),"klass should be in dictionary") fails
tonyp
parents:
0
diff
changeset
|
454 if (k != NULL) { |
38836cf1d8d2
6920977: G1: guarantee(k == probe->klass(),"klass should be in dictionary") fails
tonyp
parents:
0
diff
changeset
|
455 // We found the class in the system dictionary, so we should |
38836cf1d8d2
6920977: G1: guarantee(k == probe->klass(),"klass should be in dictionary") fails
tonyp
parents:
0
diff
changeset
|
456 // make sure that the klassOop matches what we already have. |
38836cf1d8d2
6920977: G1: guarantee(k == probe->klass(),"klass should be in dictionary") fails
tonyp
parents:
0
diff
changeset
|
457 guarantee(k == probe->klass(), "klass should be in dictionary"); |
38836cf1d8d2
6920977: G1: guarantee(k == probe->klass(),"klass should be in dictionary") fails
tonyp
parents:
0
diff
changeset
|
458 } else { |
38836cf1d8d2
6920977: G1: guarantee(k == probe->klass(),"klass should be in dictionary") fails
tonyp
parents:
0
diff
changeset
|
459 // If we don't find the class in the system dictionary, it |
38836cf1d8d2
6920977: G1: guarantee(k == probe->klass(),"klass should be in dictionary") fails
tonyp
parents:
0
diff
changeset
|
460 // has to be in the placeholders table. |
38836cf1d8d2
6920977: G1: guarantee(k == probe->klass(),"klass should be in dictionary") fails
tonyp
parents:
0
diff
changeset
|
461 unsigned int p_hash = placeholders->compute_hash(name, loader); |
38836cf1d8d2
6920977: G1: guarantee(k == probe->klass(),"klass should be in dictionary") fails
tonyp
parents:
0
diff
changeset
|
462 int p_index = placeholders->hash_to_index(p_hash); |
38836cf1d8d2
6920977: G1: guarantee(k == probe->klass(),"klass should be in dictionary") fails
tonyp
parents:
0
diff
changeset
|
463 PlaceholderEntry* entry = placeholders->get_entry(p_index, p_hash, |
38836cf1d8d2
6920977: G1: guarantee(k == probe->klass(),"klass should be in dictionary") fails
tonyp
parents:
0
diff
changeset
|
464 name, loader); |
38836cf1d8d2
6920977: G1: guarantee(k == probe->klass(),"klass should be in dictionary") fails
tonyp
parents:
0
diff
changeset
|
465 |
38836cf1d8d2
6920977: G1: guarantee(k == probe->klass(),"klass should be in dictionary") fails
tonyp
parents:
0
diff
changeset
|
466 // The instanceKlass might not be on the entry, so the only |
38836cf1d8d2
6920977: G1: guarantee(k == probe->klass(),"klass should be in dictionary") fails
tonyp
parents:
0
diff
changeset
|
467 // thing we can check here is whether we were successful in |
38836cf1d8d2
6920977: G1: guarantee(k == probe->klass(),"klass should be in dictionary") fails
tonyp
parents:
0
diff
changeset
|
468 // finding the class in the placeholders table. |
38836cf1d8d2
6920977: G1: guarantee(k == probe->klass(),"klass should be in dictionary") fails
tonyp
parents:
0
diff
changeset
|
469 guarantee(entry != NULL, "klass should be in the placeholders"); |
38836cf1d8d2
6920977: G1: guarantee(k == probe->klass(),"klass should be in dictionary") fails
tonyp
parents:
0
diff
changeset
|
470 } |
0 | 471 } |
472 for (int n = 0; n< probe->num_loaders(); n++) { | |
473 guarantee(probe->loader(n)->is_oop_or_null(), "should be oop"); | |
474 } | |
475 } | |
476 } | |
477 } | |
478 | |
479 #ifndef PRODUCT | |
480 | |
481 // Called with the system dictionary lock held | |
482 void LoaderConstraintTable::print() { | |
483 ResourceMark rm; | |
484 | |
485 assert_locked_or_safepoint(SystemDictionary_lock); | |
486 tty->print_cr("Java loader constraints (entries=%d)", _loader_constraint_size); | |
487 for (int cindex = 0; cindex < _loader_constraint_size; cindex++) { | |
488 for (LoaderConstraintEntry* probe = bucket(cindex); | |
489 probe != NULL; | |
490 probe = probe->next()) { | |
491 tty->print("%4d: ", cindex); | |
492 probe->name()->print(); | |
493 tty->print(" , loaders:"); | |
494 for (int n = 0; n < probe->num_loaders(); n++) { | |
495 probe->loader(n)->print_value(); | |
496 tty->print(", "); | |
497 } | |
498 tty->cr(); | |
499 } | |
500 } | |
501 } | |
502 #endif |