Mercurial > hg > truffle
comparison src/share/vm/ci/ciReplay.cpp @ 6972:bd7a7ce2e264
6830717: replay of compilations would help with debugging
Summary: When java process crashed in compiler thread, repeat the compilation process will help finding root cause. This is done with using SA dump application class data and replay data from core dump, then use debug version of jvm to recompile the problematic java method.
Reviewed-by: kvn, twisti, sspitsyn
Contributed-by: yumin.qi@oracle.com
author | minqi |
---|---|
date | Mon, 12 Nov 2012 14:03:53 -0800 |
parents | |
children | 90273fc0a981 |
comparison
equal
deleted
inserted
replaced
6965:3be318ecfae5 | 6972:bd7a7ce2e264 |
---|---|
1 /* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. | |
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
3 * | |
4 * This code is free software; you can redistribute it and/or modify it | |
5 * under the terms of the GNU General Public License version 2 only, as | |
6 * published by the Free Software Foundation. | |
7 * | |
8 * This code is distributed in the hope that it will be useful, but WITHOUT | |
9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
10 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
11 * version 2 for more details (a copy is included in the LICENSE file that | |
12 * accompanied this code). | |
13 * | |
14 * You should have received a copy of the GNU General Public License version | |
15 * 2 along with this work; if not, write to the Free Software Foundation, | |
16 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
17 * | |
18 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
19 * or visit www.oracle.com if you need additional information or have any | |
20 * questions. | |
21 * | |
22 */ | |
23 | |
24 #include "precompiled.hpp" | |
25 #include "ci/ciMethodData.hpp" | |
26 #include "ci/ciReplay.hpp" | |
27 #include "ci/ciUtilities.hpp" | |
28 #include "compiler/compileBroker.hpp" | |
29 #include "memory/allocation.inline.hpp" | |
30 #include "memory/oopFactory.hpp" | |
31 #include "memory/resourceArea.hpp" | |
32 #include "utilities/copy.hpp" | |
33 | |
34 #ifdef ASSERT | |
35 | |
36 // ciReplay | |
37 | |
38 typedef struct _ciMethodDataRecord { | |
39 const char* klass; | |
40 const char* method; | |
41 const char* signature; | |
42 int state; | |
43 int current_mileage; | |
44 intptr_t* data; | |
45 int data_length; | |
46 char* orig_data; | |
47 int orig_data_length; | |
48 int oops_length; | |
49 jobject* oops_handles; | |
50 int* oops_offsets; | |
51 } ciMethodDataRecord; | |
52 | |
53 typedef struct _ciMethodRecord { | |
54 const char* klass; | |
55 const char* method; | |
56 const char* signature; | |
57 int instructions_size; | |
58 int interpreter_invocation_count; | |
59 int interpreter_throwout_count; | |
60 int invocation_counter; | |
61 int backedge_counter; | |
62 } ciMethodRecord; | |
63 | |
64 class CompileReplay; | |
65 static CompileReplay* replay_state; | |
66 | |
67 class CompileReplay : public StackObj { | |
68 private: | |
69 FILE* stream; | |
70 Thread* thread; | |
71 Handle protection_domain; | |
72 Handle loader; | |
73 | |
74 GrowableArray<ciMethodRecord*> ci_method_records; | |
75 GrowableArray<ciMethodDataRecord*> ci_method_data_records; | |
76 | |
77 const char* _error_message; | |
78 | |
79 char* bufptr; | |
80 char* buffer; | |
81 int buffer_length; | |
82 int buffer_end; | |
83 int line_no; | |
84 | |
85 public: | |
86 CompileReplay(const char* filename, TRAPS) { | |
87 thread = THREAD; | |
88 loader = Handle(thread, SystemDictionary::java_system_loader()); | |
89 stream = fopen(filename, "rt"); | |
90 if (stream == NULL) { | |
91 fprintf(stderr, "Can't open replay file %s\n", filename); | |
92 } | |
93 buffer_length = 32; | |
94 buffer = NEW_RESOURCE_ARRAY(char, buffer_length); | |
95 _error_message = NULL; | |
96 | |
97 test(); | |
98 } | |
99 | |
100 ~CompileReplay() { | |
101 if (stream != NULL) fclose(stream); | |
102 } | |
103 | |
104 void test() { | |
105 strcpy(buffer, "1 2 foo 4 bar 0x9 \"this is it\""); | |
106 bufptr = buffer; | |
107 assert(parse_int("test") == 1, "what"); | |
108 assert(parse_int("test") == 2, "what"); | |
109 assert(strcmp(parse_string(), "foo") == 0, "what"); | |
110 assert(parse_int("test") == 4, "what"); | |
111 assert(strcmp(parse_string(), "bar") == 0, "what"); | |
112 assert(parse_intptr_t("test") == 9, "what"); | |
113 assert(strcmp(parse_quoted_string(), "this is it") == 0, "what"); | |
114 } | |
115 | |
116 bool had_error() { | |
117 return _error_message != NULL || thread->has_pending_exception(); | |
118 } | |
119 | |
120 bool can_replay() { | |
121 return !(stream == NULL || had_error()); | |
122 } | |
123 | |
124 void report_error(const char* msg) { | |
125 _error_message = msg; | |
126 // Restore the buffer contents for error reporting | |
127 for (int i = 0; i < buffer_end; i++) { | |
128 if (buffer[i] == '\0') buffer[i] = ' '; | |
129 } | |
130 } | |
131 | |
132 int parse_int(const char* label) { | |
133 if (had_error()) { | |
134 return 0; | |
135 } | |
136 | |
137 int v = 0; | |
138 int read; | |
139 if (sscanf(bufptr, "%i%n", &v, &read) != 1) { | |
140 report_error(label); | |
141 } else { | |
142 bufptr += read; | |
143 } | |
144 return v; | |
145 } | |
146 | |
147 intptr_t parse_intptr_t(const char* label) { | |
148 if (had_error()) { | |
149 return 0; | |
150 } | |
151 | |
152 intptr_t v = 0; | |
153 int read; | |
154 if (sscanf(bufptr, INTPTR_FORMAT "%n", &v, &read) != 1) { | |
155 report_error(label); | |
156 } else { | |
157 bufptr += read; | |
158 } | |
159 return v; | |
160 } | |
161 | |
162 void skip_ws() { | |
163 // Skip any leading whitespace | |
164 while (*bufptr == ' ' || *bufptr == '\t') { | |
165 bufptr++; | |
166 } | |
167 } | |
168 | |
169 | |
170 char* scan_and_terminate(char delim) { | |
171 char* str = bufptr; | |
172 while (*bufptr != delim && *bufptr != '\0') { | |
173 bufptr++; | |
174 } | |
175 if (*bufptr != '\0') { | |
176 *bufptr++ = '\0'; | |
177 } | |
178 if (bufptr == str) { | |
179 // nothing here | |
180 return NULL; | |
181 } | |
182 return str; | |
183 } | |
184 | |
185 char* parse_string() { | |
186 if (had_error()) return NULL; | |
187 | |
188 skip_ws(); | |
189 return scan_and_terminate(' '); | |
190 } | |
191 | |
192 char* parse_quoted_string() { | |
193 if (had_error()) return NULL; | |
194 | |
195 skip_ws(); | |
196 | |
197 if (*bufptr == '"') { | |
198 bufptr++; | |
199 return scan_and_terminate('"'); | |
200 } else { | |
201 return scan_and_terminate(' '); | |
202 } | |
203 } | |
204 | |
205 const char* parse_escaped_string() { | |
206 char* result = parse_quoted_string(); | |
207 if (result != NULL) { | |
208 unescape_string(result); | |
209 } | |
210 return result; | |
211 } | |
212 | |
213 // Look for the tag 'tag' followed by an | |
214 bool parse_tag_and_count(const char* tag, int& length) { | |
215 const char* t = parse_string(); | |
216 if (t == NULL) { | |
217 return false; | |
218 } | |
219 | |
220 if (strcmp(tag, t) != 0) { | |
221 report_error(tag); | |
222 return false; | |
223 } | |
224 length = parse_int("parse_tag_and_count"); | |
225 return !had_error(); | |
226 } | |
227 | |
228 // Parse a sequence of raw data encoded as bytes and return the | |
229 // resulting data. | |
230 char* parse_data(const char* tag, int& length) { | |
231 if (!parse_tag_and_count(tag, length)) { | |
232 return NULL; | |
233 } | |
234 | |
235 char * result = NEW_RESOURCE_ARRAY(char, length); | |
236 for (int i = 0; i < length; i++) { | |
237 int val = parse_int("data"); | |
238 result[i] = val; | |
239 } | |
240 return result; | |
241 } | |
242 | |
243 // Parse a standard chunk of data emitted as: | |
244 // 'tag' <length> # # ... | |
245 // Where each # is an intptr_t item | |
246 intptr_t* parse_intptr_data(const char* tag, int& length) { | |
247 if (!parse_tag_and_count(tag, length)) { | |
248 return NULL; | |
249 } | |
250 | |
251 intptr_t* result = NEW_RESOURCE_ARRAY(intptr_t, length); | |
252 for (int i = 0; i < length; i++) { | |
253 skip_ws(); | |
254 intptr_t val = parse_intptr_t("data"); | |
255 result[i] = val; | |
256 } | |
257 return result; | |
258 } | |
259 | |
260 // Parse a possibly quoted version of a symbol into a symbolOop | |
261 Symbol* parse_symbol(TRAPS) { | |
262 const char* str = parse_escaped_string(); | |
263 if (str != NULL) { | |
264 Symbol* sym = SymbolTable::lookup(str, (int)strlen(str), CHECK_NULL); | |
265 return sym; | |
266 } | |
267 return NULL; | |
268 } | |
269 | |
270 // Parse a valid klass name and look it up | |
271 Klass* parse_klass(TRAPS) { | |
272 const char* str = parse_escaped_string(); | |
273 Symbol* klass_name = SymbolTable::lookup(str, (int)strlen(str), CHECK_NULL); | |
274 if (klass_name != NULL) { | |
275 Klass* k = SystemDictionary::resolve_or_fail(klass_name, loader, protection_domain, true, THREAD); | |
276 if (HAS_PENDING_EXCEPTION) { | |
277 oop throwable = PENDING_EXCEPTION; | |
278 java_lang_Throwable::print(throwable, tty); | |
279 tty->cr(); | |
280 report_error(str); | |
281 return NULL; | |
282 } | |
283 return k; | |
284 } | |
285 return NULL; | |
286 } | |
287 | |
288 // Lookup a klass | |
289 Klass* resolve_klass(const char* klass, TRAPS) { | |
290 Symbol* klass_name = SymbolTable::lookup(klass, (int)strlen(klass), CHECK_NULL); | |
291 return SystemDictionary::resolve_or_fail(klass_name, loader, protection_domain, true, CHECK_NULL); | |
292 } | |
293 | |
294 // Parse the standard tuple of <klass> <name> <signature> | |
295 Method* parse_method(TRAPS) { | |
296 InstanceKlass* k = (InstanceKlass*)parse_klass(CHECK_NULL); | |
297 Symbol* method_name = parse_symbol(CHECK_NULL); | |
298 Symbol* method_signature = parse_symbol(CHECK_NULL); | |
299 Method* m = k->find_method(method_name, method_signature); | |
300 if (m == NULL) { | |
301 report_error("can't find method"); | |
302 } | |
303 return m; | |
304 } | |
305 | |
306 // Process each line of the replay file executing each command until | |
307 // the file ends. | |
308 void process(TRAPS) { | |
309 line_no = 1; | |
310 int pos = 0; | |
311 int c = getc(stream); | |
312 while(c != EOF) { | |
313 if (pos + 1 >= buffer_length) { | |
314 int newl = buffer_length * 2; | |
315 char* newb = NEW_RESOURCE_ARRAY(char, newl); | |
316 memcpy(newb, buffer, pos); | |
317 buffer = newb; | |
318 buffer_length = newl; | |
319 } | |
320 if (c == '\n') { | |
321 // null terminate it, reset the pointer and process the line | |
322 buffer[pos] = '\0'; | |
323 buffer_end = pos++; | |
324 bufptr = buffer; | |
325 process_command(CHECK); | |
326 if (had_error()) { | |
327 tty->print_cr("Error while parsing line %d: %s\n", line_no, _error_message); | |
328 tty->print_cr("%s", buffer); | |
329 assert(false, "error"); | |
330 return; | |
331 } | |
332 pos = 0; | |
333 buffer_end = 0; | |
334 line_no++; | |
335 } else if (c == '\r') { | |
336 // skip LF | |
337 } else { | |
338 buffer[pos++] = c; | |
339 } | |
340 c = getc(stream); | |
341 } | |
342 } | |
343 | |
344 void process_command(TRAPS) { | |
345 char* cmd = parse_string(); | |
346 if (cmd == NULL) { | |
347 return; | |
348 } | |
349 if (strcmp("#", cmd) == 0) { | |
350 // ignore | |
351 } else if (strcmp("compile", cmd) == 0) { | |
352 process_compile(CHECK); | |
353 } else if (strcmp("ciMethod", cmd) == 0) { | |
354 process_ciMethod(CHECK); | |
355 } else if (strcmp("ciMethodData", cmd) == 0) { | |
356 process_ciMethodData(CHECK); | |
357 } else if (strcmp("staticfield", cmd) == 0) { | |
358 process_staticfield(CHECK); | |
359 } else if (strcmp("ciInstanceKlass", cmd) == 0) { | |
360 process_ciInstanceKlass(CHECK); | |
361 } else if (strcmp("instanceKlass", cmd) == 0) { | |
362 process_instanceKlass(CHECK); | |
363 #if INCLUDE_JVMTI | |
364 } else if (strcmp("JvmtiExport", cmd) == 0) { | |
365 process_JvmtiExport(CHECK); | |
366 #endif // INCLUDE_JVMTI | |
367 } else { | |
368 report_error("unknown command"); | |
369 } | |
370 } | |
371 | |
372 // compile <klass> <name> <signature> <entry_bci> | |
373 void process_compile(TRAPS) { | |
374 // methodHandle method; | |
375 Method* method = parse_method(CHECK); | |
376 int entry_bci = parse_int("entry_bci"); | |
377 Klass* k = method->method_holder(); | |
378 ((InstanceKlass*)k)->initialize(THREAD); | |
379 if (HAS_PENDING_EXCEPTION) { | |
380 oop throwable = PENDING_EXCEPTION; | |
381 java_lang_Throwable::print(throwable, tty); | |
382 tty->cr(); | |
383 if (ReplayIgnoreInitErrors) { | |
384 CLEAR_PENDING_EXCEPTION; | |
385 ((InstanceKlass*)k)->set_init_state(InstanceKlass::fully_initialized); | |
386 } else { | |
387 return; | |
388 } | |
389 } | |
390 // Make sure the existence of a prior compile doesn't stop this one | |
391 nmethod* nm = (entry_bci != InvocationEntryBci) ? method->lookup_osr_nmethod_for(entry_bci, CompLevel_full_optimization, true) : method->code(); | |
392 if (nm != NULL) { | |
393 nm->make_not_entrant(); | |
394 } | |
395 replay_state = this; | |
396 CompileBroker::compile_method(method, entry_bci, CompLevel_full_optimization, | |
397 methodHandle(), 0, "replay", THREAD); | |
398 replay_state = NULL; | |
399 reset(); | |
400 } | |
401 | |
402 // ciMethod <klass> <name> <signature> <invocation_counter> <backedge_counter> <interpreter_invocation_count> <interpreter_throwout_count> <instructions_size> | |
403 // | |
404 // | |
405 void process_ciMethod(TRAPS) { | |
406 Method* method = parse_method(CHECK); | |
407 ciMethodRecord* rec = new_ciMethod(method); | |
408 rec->invocation_counter = parse_int("invocation_counter"); | |
409 rec->backedge_counter = parse_int("backedge_counter"); | |
410 rec->interpreter_invocation_count = parse_int("interpreter_invocation_count"); | |
411 rec->interpreter_throwout_count = parse_int("interpreter_throwout_count"); | |
412 rec->instructions_size = parse_int("instructions_size"); | |
413 } | |
414 | |
415 // ciMethodData <klass> <name> <signature> <state> <current mileage> orig <length> # # ... data <length> # # ... oops <length> | |
416 void process_ciMethodData(TRAPS) { | |
417 Method* method = parse_method(CHECK); | |
418 /* jsut copied from Method, to build interpret data*/ | |
419 if (InstanceRefKlass::owns_pending_list_lock((JavaThread*)THREAD)) { | |
420 return; | |
421 } | |
422 // methodOopDesc::build_interpreter_method_data(method, CHECK); | |
423 { | |
424 // Grab a lock here to prevent multiple | |
425 // MethodData*s from being created. | |
426 MutexLocker ml(MethodData_lock, THREAD); | |
427 if (method->method_data() == NULL) { | |
428 ClassLoaderData* loader_data = method->method_holder()->class_loader_data(); | |
429 MethodData* method_data = MethodData::allocate(loader_data, method, CHECK); | |
430 method->set_method_data(method_data); | |
431 } | |
432 } | |
433 | |
434 // collect and record all the needed information for later | |
435 ciMethodDataRecord* rec = new_ciMethodData(method); | |
436 rec->state = parse_int("state"); | |
437 rec->current_mileage = parse_int("current_mileage"); | |
438 | |
439 rec->orig_data = parse_data("orig", rec->orig_data_length); | |
440 if (rec->orig_data == NULL) { | |
441 return; | |
442 } | |
443 rec->data = parse_intptr_data("data", rec->data_length); | |
444 if (rec->data == NULL) { | |
445 return; | |
446 } | |
447 if (!parse_tag_and_count("oops", rec->oops_length)) { | |
448 return; | |
449 } | |
450 rec->oops_handles = NEW_RESOURCE_ARRAY(jobject, rec->oops_length); | |
451 rec->oops_offsets = NEW_RESOURCE_ARRAY(int, rec->oops_length); | |
452 for (int i = 0; i < rec->oops_length; i++) { | |
453 int offset = parse_int("offset"); | |
454 if (had_error()) { | |
455 return; | |
456 } | |
457 Klass* k = parse_klass(CHECK); | |
458 rec->oops_offsets[i] = offset; | |
459 rec->oops_handles[i] = (jobject)(new KlassHandle(THREAD, k)); | |
460 } | |
461 } | |
462 | |
463 // instanceKlass <name> | |
464 // | |
465 // Loads and initializes the klass 'name'. This can be used to | |
466 // create particular class loading environments | |
467 void process_instanceKlass(TRAPS) { | |
468 // just load the referenced class | |
469 Klass* k = parse_klass(CHECK); | |
470 } | |
471 | |
472 // ciInstanceKlass <name> <is_linked> <is_initialized> <length> tag # # # ... | |
473 // | |
474 // Load the klass 'name' and link or initialize it. Verify that the | |
475 // constant pool is the same length as 'length' and make sure the | |
476 // constant pool tags are in the same state. | |
477 void process_ciInstanceKlass(TRAPS) { | |
478 InstanceKlass* k = (InstanceKlass *)parse_klass(CHECK); | |
479 int is_linked = parse_int("is_linked"); | |
480 int is_initialized = parse_int("is_initialized"); | |
481 int length = parse_int("length"); | |
482 if (is_initialized) { | |
483 k->initialize(THREAD); | |
484 if (HAS_PENDING_EXCEPTION) { | |
485 oop throwable = PENDING_EXCEPTION; | |
486 java_lang_Throwable::print(throwable, tty); | |
487 tty->cr(); | |
488 if (ReplayIgnoreInitErrors) { | |
489 CLEAR_PENDING_EXCEPTION; | |
490 k->set_init_state(InstanceKlass::fully_initialized); | |
491 } else { | |
492 return; | |
493 } | |
494 } | |
495 } else if (is_linked) { | |
496 k->link_class(CHECK); | |
497 } | |
498 ConstantPool* cp = k->constants(); | |
499 if (length != cp->length()) { | |
500 report_error("constant pool length mismatch: wrong class files?"); | |
501 return; | |
502 } | |
503 | |
504 int parsed_two_word = 0; | |
505 for (int i = 1; i < length; i++) { | |
506 int tag = parse_int("tag"); | |
507 if (had_error()) { | |
508 return; | |
509 } | |
510 switch (cp->tag_at(i).value()) { | |
511 case JVM_CONSTANT_UnresolvedClass: { | |
512 if (tag == JVM_CONSTANT_Class) { | |
513 tty->print_cr("Resolving klass %s at %d", cp->unresolved_klass_at(i)->as_utf8(), i); | |
514 Klass* k = cp->klass_at(i, CHECK); | |
515 } | |
516 break; | |
517 } | |
518 case JVM_CONSTANT_Long: | |
519 case JVM_CONSTANT_Double: | |
520 parsed_two_word = i + 1; | |
521 | |
522 case JVM_CONSTANT_ClassIndex: | |
523 case JVM_CONSTANT_StringIndex: | |
524 case JVM_CONSTANT_String: | |
525 case JVM_CONSTANT_UnresolvedClassInError: | |
526 case JVM_CONSTANT_Fieldref: | |
527 case JVM_CONSTANT_Methodref: | |
528 case JVM_CONSTANT_InterfaceMethodref: | |
529 case JVM_CONSTANT_NameAndType: | |
530 case JVM_CONSTANT_Utf8: | |
531 case JVM_CONSTANT_Integer: | |
532 case JVM_CONSTANT_Float: | |
533 if (tag != cp->tag_at(i).value()) { | |
534 report_error("tag mismatch: wrong class files?"); | |
535 return; | |
536 } | |
537 break; | |
538 | |
539 case JVM_CONSTANT_Class: | |
540 if (tag == JVM_CONSTANT_Class) { | |
541 } else if (tag == JVM_CONSTANT_UnresolvedClass) { | |
542 tty->print_cr("Warning: entry was unresolved in the replay data"); | |
543 } else { | |
544 report_error("Unexpected tag"); | |
545 return; | |
546 } | |
547 break; | |
548 | |
549 case 0: | |
550 if (parsed_two_word == i) continue; | |
551 | |
552 default: | |
553 ShouldNotReachHere(); | |
554 break; | |
555 } | |
556 | |
557 } | |
558 } | |
559 | |
560 // Initialize a class and fill in the value for a static field. | |
561 // This is useful when the compile was dependent on the value of | |
562 // static fields but it's impossible to properly rerun the static | |
563 // initiailizer. | |
564 void process_staticfield(TRAPS) { | |
565 InstanceKlass* k = (InstanceKlass *)parse_klass(CHECK); | |
566 | |
567 if (ReplaySuppressInitializers == 0 || | |
568 ReplaySuppressInitializers == 2 && k->class_loader() == NULL) { | |
569 return; | |
570 } | |
571 | |
572 assert(k->is_initialized(), "must be"); | |
573 | |
574 const char* field_name = parse_escaped_string();; | |
575 const char* field_signature = parse_string(); | |
576 fieldDescriptor fd; | |
577 Symbol* name = SymbolTable::lookup(field_name, (int)strlen(field_name), CHECK); | |
578 Symbol* sig = SymbolTable::lookup(field_signature, (int)strlen(field_signature), CHECK); | |
579 if (!k->find_local_field(name, sig, &fd) || | |
580 !fd.is_static() || | |
581 fd.has_initial_value()) { | |
582 report_error(field_name); | |
583 return; | |
584 } | |
585 | |
586 oop java_mirror = k->java_mirror(); | |
587 if (field_signature[0] == '[') { | |
588 int length = parse_int("array length"); | |
589 oop value = NULL; | |
590 | |
591 if (field_signature[1] == '[') { | |
592 // multi dimensional array | |
593 ArrayKlass* kelem = (ArrayKlass *)parse_klass(CHECK); | |
594 int rank = 0; | |
595 while (field_signature[rank] == '[') { | |
596 rank++; | |
597 } | |
598 int* dims = NEW_RESOURCE_ARRAY(int, rank); | |
599 dims[0] = length; | |
600 for (int i = 1; i < rank; i++) { | |
601 dims[i] = 1; // These aren't relevant to the compiler | |
602 } | |
603 value = kelem->multi_allocate(rank, dims, CHECK); | |
604 } else { | |
605 if (strcmp(field_signature, "[B") == 0) { | |
606 value = oopFactory::new_byteArray(length, CHECK); | |
607 } else if (strcmp(field_signature, "[Z") == 0) { | |
608 value = oopFactory::new_boolArray(length, CHECK); | |
609 } else if (strcmp(field_signature, "[C") == 0) { | |
610 value = oopFactory::new_charArray(length, CHECK); | |
611 } else if (strcmp(field_signature, "[S") == 0) { | |
612 value = oopFactory::new_shortArray(length, CHECK); | |
613 } else if (strcmp(field_signature, "[F") == 0) { | |
614 value = oopFactory::new_singleArray(length, CHECK); | |
615 } else if (strcmp(field_signature, "[D") == 0) { | |
616 value = oopFactory::new_doubleArray(length, CHECK); | |
617 } else if (strcmp(field_signature, "[I") == 0) { | |
618 value = oopFactory::new_intArray(length, CHECK); | |
619 } else if (strcmp(field_signature, "[J") == 0) { | |
620 value = oopFactory::new_longArray(length, CHECK); | |
621 } else if (field_signature[0] == '[' && field_signature[1] == 'L') { | |
622 KlassHandle kelem = resolve_klass(field_signature + 1, CHECK); | |
623 value = oopFactory::new_objArray(kelem(), length, CHECK); | |
624 } else { | |
625 report_error("unhandled array staticfield"); | |
626 } | |
627 } | |
628 java_mirror->obj_field_put(fd.offset(), value); | |
629 } else { | |
630 const char* string_value = parse_escaped_string(); | |
631 if (strcmp(field_signature, "I") == 0) { | |
632 int value = atoi(string_value); | |
633 java_mirror->int_field_put(fd.offset(), value); | |
634 } else if (strcmp(field_signature, "B") == 0) { | |
635 int value = atoi(string_value); | |
636 java_mirror->byte_field_put(fd.offset(), value); | |
637 } else if (strcmp(field_signature, "C") == 0) { | |
638 int value = atoi(string_value); | |
639 java_mirror->char_field_put(fd.offset(), value); | |
640 } else if (strcmp(field_signature, "S") == 0) { | |
641 int value = atoi(string_value); | |
642 java_mirror->short_field_put(fd.offset(), value); | |
643 } else if (strcmp(field_signature, "Z") == 0) { | |
644 int value = atol(string_value); | |
645 java_mirror->bool_field_put(fd.offset(), value); | |
646 } else if (strcmp(field_signature, "J") == 0) { | |
647 jlong value; | |
648 if (sscanf(string_value, INT64_FORMAT, &value) != 1) { | |
649 fprintf(stderr, "Error parsing long: %s\n", string_value); | |
650 return; | |
651 } | |
652 java_mirror->long_field_put(fd.offset(), value); | |
653 } else if (strcmp(field_signature, "F") == 0) { | |
654 float value = atof(string_value); | |
655 java_mirror->float_field_put(fd.offset(), value); | |
656 } else if (strcmp(field_signature, "D") == 0) { | |
657 double value = atof(string_value); | |
658 java_mirror->double_field_put(fd.offset(), value); | |
659 } else if (strcmp(field_signature, "Ljava/lang/String;") == 0) { | |
660 Handle value = java_lang_String::create_from_str(string_value, CHECK); | |
661 java_mirror->obj_field_put(fd.offset(), value()); | |
662 } else if (field_signature[0] == 'L') { | |
663 Symbol* klass_name = SymbolTable::lookup(field_signature, (int)strlen(field_signature), CHECK); | |
664 KlassHandle kelem = resolve_klass(field_signature, CHECK); | |
665 oop value = ((InstanceKlass*)kelem())->allocate_instance(CHECK); | |
666 java_mirror->obj_field_put(fd.offset(), value); | |
667 } else { | |
668 report_error("unhandled staticfield"); | |
669 } | |
670 } | |
671 } | |
672 | |
673 #if INCLUDE_JVMTI | |
674 void process_JvmtiExport(TRAPS) { | |
675 const char* field = parse_string(); | |
676 bool value = parse_int("JvmtiExport flag") != 0; | |
677 if (strcmp(field, "can_access_local_variables") == 0) { | |
678 JvmtiExport::set_can_access_local_variables(value); | |
679 } else if (strcmp(field, "can_hotswap_or_post_breakpoint") == 0) { | |
680 JvmtiExport::set_can_hotswap_or_post_breakpoint(value); | |
681 } else if (strcmp(field, "can_post_on_exceptions") == 0) { | |
682 JvmtiExport::set_can_post_on_exceptions(value); | |
683 } else { | |
684 report_error("Unrecognized JvmtiExport directive"); | |
685 } | |
686 } | |
687 #endif // INCLUDE_JVMTI | |
688 | |
689 // Create and initialize a record for a ciMethod | |
690 ciMethodRecord* new_ciMethod(Method* method) { | |
691 ciMethodRecord* rec = NEW_RESOURCE_OBJ(ciMethodRecord); | |
692 rec->klass = method->method_holder()->name()->as_utf8(); | |
693 rec->method = method->name()->as_utf8(); | |
694 rec->signature = method->signature()->as_utf8(); | |
695 ci_method_records.append(rec); | |
696 return rec; | |
697 } | |
698 | |
699 // Lookup data for a ciMethod | |
700 ciMethodRecord* find_ciMethodRecord(Method* method) { | |
701 const char* klass_name = method->method_holder()->name()->as_utf8(); | |
702 const char* method_name = method->name()->as_utf8(); | |
703 const char* signature = method->signature()->as_utf8(); | |
704 for (int i = 0; i < ci_method_records.length(); i++) { | |
705 ciMethodRecord* rec = ci_method_records.at(i); | |
706 if (strcmp(rec->klass, klass_name) == 0 && | |
707 strcmp(rec->method, method_name) == 0 && | |
708 strcmp(rec->signature, signature) == 0) { | |
709 return rec; | |
710 } | |
711 } | |
712 return NULL; | |
713 } | |
714 | |
715 // Create and initialize a record for a ciMethodData | |
716 ciMethodDataRecord* new_ciMethodData(Method* method) { | |
717 ciMethodDataRecord* rec = NEW_RESOURCE_OBJ(ciMethodDataRecord); | |
718 rec->klass = method->method_holder()->name()->as_utf8(); | |
719 rec->method = method->name()->as_utf8(); | |
720 rec->signature = method->signature()->as_utf8(); | |
721 ci_method_data_records.append(rec); | |
722 return rec; | |
723 } | |
724 | |
725 // Lookup data for a ciMethodData | |
726 ciMethodDataRecord* find_ciMethodDataRecord(Method* method) { | |
727 const char* klass_name = method->method_holder()->name()->as_utf8(); | |
728 const char* method_name = method->name()->as_utf8(); | |
729 const char* signature = method->signature()->as_utf8(); | |
730 for (int i = 0; i < ci_method_data_records.length(); i++) { | |
731 ciMethodDataRecord* rec = ci_method_data_records.at(i); | |
732 if (strcmp(rec->klass, klass_name) == 0 && | |
733 strcmp(rec->method, method_name) == 0 && | |
734 strcmp(rec->signature, signature) == 0) { | |
735 return rec; | |
736 } | |
737 } | |
738 return NULL; | |
739 } | |
740 | |
741 const char* error_message() { | |
742 return _error_message; | |
743 } | |
744 | |
745 void reset() { | |
746 _error_message = NULL; | |
747 ci_method_records.clear(); | |
748 ci_method_data_records.clear(); | |
749 } | |
750 | |
751 // Take an ascii string contain \u#### escapes and convert it to utf8 | |
752 // in place. | |
753 static void unescape_string(char* value) { | |
754 char* from = value; | |
755 char* to = value; | |
756 while (*from != '\0') { | |
757 if (*from != '\\') { | |
758 *from++ = *to++; | |
759 } else { | |
760 switch (from[1]) { | |
761 case 'u': { | |
762 from += 2; | |
763 jchar value=0; | |
764 for (int i=0; i<4; i++) { | |
765 char c = *from++; | |
766 switch (c) { | |
767 case '0': case '1': case '2': case '3': case '4': | |
768 case '5': case '6': case '7': case '8': case '9': | |
769 value = (value << 4) + c - '0'; | |
770 break; | |
771 case 'a': case 'b': case 'c': | |
772 case 'd': case 'e': case 'f': | |
773 value = (value << 4) + 10 + c - 'a'; | |
774 break; | |
775 case 'A': case 'B': case 'C': | |
776 case 'D': case 'E': case 'F': | |
777 value = (value << 4) + 10 + c - 'A'; | |
778 break; | |
779 default: | |
780 ShouldNotReachHere(); | |
781 } | |
782 } | |
783 UNICODE::convert_to_utf8(&value, 1, to); | |
784 to++; | |
785 break; | |
786 } | |
787 case 't': *to++ = '\t'; from += 2; break; | |
788 case 'n': *to++ = '\n'; from += 2; break; | |
789 case 'r': *to++ = '\r'; from += 2; break; | |
790 case 'f': *to++ = '\f'; from += 2; break; | |
791 default: | |
792 ShouldNotReachHere(); | |
793 } | |
794 } | |
795 } | |
796 *from = *to; | |
797 } | |
798 }; | |
799 | |
800 void ciReplay::replay(TRAPS) { | |
801 int exit_code = replay_impl(THREAD); | |
802 | |
803 Threads::destroy_vm(); | |
804 | |
805 vm_exit(exit_code); | |
806 } | |
807 | |
808 int ciReplay::replay_impl(TRAPS) { | |
809 HandleMark hm; | |
810 ResourceMark rm; | |
811 // Make sure we don't run with background compilation | |
812 BackgroundCompilation = false; | |
813 | |
814 if (ReplaySuppressInitializers > 2) { | |
815 // ReplaySuppressInitializers > 2 means that we want to allow | |
816 // normal VM bootstrap but once we get into the replay itself | |
817 // don't allow any intializers to be run. | |
818 ReplaySuppressInitializers = 1; | |
819 } | |
820 | |
821 // Load and parse the replay data | |
822 CompileReplay rp(ReplayDataFile, THREAD); | |
823 int exit_code = 0; | |
824 if (rp.can_replay()) { | |
825 rp.process(THREAD); | |
826 } else { | |
827 exit_code = 1; | |
828 return exit_code; | |
829 } | |
830 | |
831 if (HAS_PENDING_EXCEPTION) { | |
832 oop throwable = PENDING_EXCEPTION; | |
833 CLEAR_PENDING_EXCEPTION; | |
834 java_lang_Throwable::print(throwable, tty); | |
835 tty->cr(); | |
836 java_lang_Throwable::print_stack_trace(throwable, tty); | |
837 tty->cr(); | |
838 exit_code = 2; | |
839 } | |
840 | |
841 if (rp.had_error()) { | |
842 tty->print_cr("Failed on %s", rp.error_message()); | |
843 exit_code = 1; | |
844 } | |
845 return exit_code; | |
846 } | |
847 | |
848 | |
849 void ciReplay::initialize(ciMethodData* m) { | |
850 if (replay_state == NULL) { | |
851 return; | |
852 } | |
853 | |
854 ASSERT_IN_VM; | |
855 ResourceMark rm; | |
856 | |
857 Method* method = m->get_MethodData()->method(); | |
858 ciMethodDataRecord* rec = replay_state->find_ciMethodDataRecord(method); | |
859 if (rec == NULL) { | |
860 // This indicates some mismatch with the original environment and | |
861 // the replay environment though it's not always enough to | |
862 // interfere with reproducing a bug | |
863 tty->print_cr("Warning: requesting ciMethodData record for method with no data: "); | |
864 method->print_name(tty); | |
865 tty->cr(); | |
866 } else { | |
867 m->_state = rec->state; | |
868 m->_current_mileage = rec->current_mileage; | |
869 if (rec->data_length != 0) { | |
870 assert(m->_data_size == rec->data_length * (int)sizeof(rec->data[0]), "must agree"); | |
871 | |
872 // Write the correct ciObjects back into the profile data | |
873 ciEnv* env = ciEnv::current(); | |
874 for (int i = 0; i < rec->oops_length; i++) { | |
875 KlassHandle *h = (KlassHandle *)rec->oops_handles[i]; | |
876 *(ciMetadata**)(rec->data + rec->oops_offsets[i]) = | |
877 env->get_metadata((*h)()); | |
878 } | |
879 // Copy the updated profile data into place as intptr_ts | |
880 #ifdef _LP64 | |
881 Copy::conjoint_jlongs_atomic((jlong *)rec->data, (jlong *)m->_data, rec->data_length); | |
882 #else | |
883 Copy::conjoint_jints_atomic((jint *)rec->data, (jint *)m->_data, rec->data_length); | |
884 #endif | |
885 } | |
886 | |
887 // copy in the original header | |
888 Copy::conjoint_jbytes(rec->orig_data, (char*)&m->_orig, rec->orig_data_length); | |
889 } | |
890 } | |
891 | |
892 | |
893 bool ciReplay::should_not_inline(ciMethod* method) { | |
894 if (replay_state == NULL) { | |
895 return false; | |
896 } | |
897 | |
898 VM_ENTRY_MARK; | |
899 // ciMethod without a record shouldn't be inlined. | |
900 return replay_state->find_ciMethodRecord(method->get_Method()) == NULL; | |
901 } | |
902 | |
903 | |
904 void ciReplay::initialize(ciMethod* m) { | |
905 if (replay_state == NULL) { | |
906 return; | |
907 } | |
908 | |
909 ASSERT_IN_VM; | |
910 ResourceMark rm; | |
911 | |
912 Method* method = m->get_Method(); | |
913 ciMethodRecord* rec = replay_state->find_ciMethodRecord(method); | |
914 if (rec == NULL) { | |
915 // This indicates some mismatch with the original environment and | |
916 // the replay environment though it's not always enough to | |
917 // interfere with reproducing a bug | |
918 tty->print_cr("Warning: requesting ciMethod record for method with no data: "); | |
919 method->print_name(tty); | |
920 tty->cr(); | |
921 } else { | |
922 // m->_instructions_size = rec->instructions_size; | |
923 m->_instructions_size = -1; | |
924 m->_interpreter_invocation_count = rec->interpreter_invocation_count; | |
925 m->_interpreter_throwout_count = rec->interpreter_throwout_count; | |
926 method->invocation_counter()->_counter = rec->invocation_counter; | |
927 method->backedge_counter()->_counter = rec->backedge_counter; | |
928 } | |
929 } | |
930 | |
931 bool ciReplay::is_loaded(Method* method) { | |
932 if (replay_state == NULL) { | |
933 return true; | |
934 } | |
935 | |
936 ASSERT_IN_VM; | |
937 ResourceMark rm; | |
938 | |
939 ciMethodRecord* rec = replay_state->find_ciMethodRecord(method); | |
940 return rec != NULL; | |
941 } | |
942 #endif |