From 00de288446b162fd66cd1149699de3379225508c Mon Sep 17 00:00:00 2001 From: minqi Date: Mon, 12 Nov 2012 14:03:53 -0800 Subject: [PATCH] 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 --- agent/doc/c2replay.html | 41 + agent/doc/clhsdb.html | 19 + agent/doc/index.html | 6 + agent/make/Makefile | 16 +- .../sun/jvm/hotspot/CommandProcessor.java | 107 ++ .../sun/jvm/hotspot/ci/ciBaseObject.java | 10 +- .../sun/jvm/hotspot/ci/ciConstant.java | 12 +- .../classes/sun/jvm/hotspot/ci/ciEnv.java | 33 +- .../sun/jvm/hotspot/ci/ciInstanceKlass.java | 86 +- .../classes/sun/jvm/hotspot/ci/ciMethod.java | 21 +- .../sun/jvm/hotspot/ci/ciMethodData.java | 54 +- .../classes/sun/jvm/hotspot/code/NMethod.java | 36 + .../sun/jvm/hotspot/compiler/CompileTask.java | 2 +- .../jvm/hotspot/oops/ConstantPoolCache.java | 2 +- .../classes/sun/jvm/hotspot/oops/Field.java | 2 + .../sun/jvm/hotspot/oops/InstanceKlass.java | 97 +- .../sun/jvm/hotspot/oops/Metadata.java | 3 + .../classes/sun/jvm/hotspot/oops/Method.java | 19 + .../sun/jvm/hotspot/oops/MethodData.java | 55 + src/share/vm/ci/ciClassList.hpp | 1 + src/share/vm/ci/ciEnv.cpp | 46 + src/share/vm/ci/ciEnv.hpp | 9 + src/share/vm/ci/ciInstanceKlass.cpp | 111 +++ src/share/vm/ci/ciInstanceKlass.hpp | 3 + src/share/vm/ci/ciMetadata.hpp | 1 + src/share/vm/ci/ciMethod.cpp | 47 +- src/share/vm/ci/ciMethod.hpp | 5 +- src/share/vm/ci/ciMethodData.cpp | 79 ++ src/share/vm/ci/ciMethodData.hpp | 2 + src/share/vm/ci/ciObject.hpp | 1 + src/share/vm/ci/ciObjectFactory.hpp | 1 + src/share/vm/ci/ciReplay.cpp | 942 ++++++++++++++++++ src/share/vm/ci/ciReplay.hpp | 55 + src/share/vm/ci/ciSymbol.cpp | 5 + src/share/vm/ci/ciSymbol.hpp | 3 + src/share/vm/ci/ciUtilities.hpp | 5 +- src/share/vm/classfile/javaClasses.cpp | 16 + src/share/vm/classfile/javaClasses.hpp | 2 + src/share/vm/code/dependencies.cpp | 1 + .../vm/interpreter/invocationCounter.hpp | 5 +- src/share/vm/oops/instanceKlass.cpp | 7 + src/share/vm/oops/instanceKlass.hpp | 1 + src/share/vm/oops/symbol.cpp | 20 +- src/share/vm/oops/symbol.hpp | 2 + src/share/vm/opto/bytecodeInfo.cpp | 13 +- src/share/vm/prims/jni.cpp | 2 + src/share/vm/prims/jvmtiExport.hpp | 2 + src/share/vm/runtime/compilationPolicy.cpp | 13 + src/share/vm/runtime/globals.hpp | 20 + src/share/vm/runtime/vmStructs.cpp | 1 + src/share/vm/utilities/array.hpp | 6 +- src/share/vm/utilities/utf8.cpp | 149 ++- src/share/vm/utilities/utf8.hpp | 32 +- src/share/vm/utilities/vmError.cpp | 9 + 54 files changed, 2160 insertions(+), 78 deletions(-) create mode 100644 agent/doc/c2replay.html create mode 100644 src/share/vm/ci/ciReplay.cpp create mode 100644 src/share/vm/ci/ciReplay.hpp diff --git a/agent/doc/c2replay.html b/agent/doc/c2replay.html new file mode 100644 index 000000000..cf90e0e8f --- /dev/null +++ b/agent/doc/c2replay.html @@ -0,0 +1,41 @@ + + + +C2 Replay + + + + +

C2 compiler replay

+

+The C2 compiler replay is a function to repeat the compiling process from a crashed java process in compiled method
+This function only exists in debug version of VM +

+

Usage

+
 
+First, use SA to attach to the core file, if suceeded, do
+       clhsdb>dumpreplaydata 
| -a | [> replay.txt] + create file replay.txt, address is address of Method, or nmethod(CodeBlob) + clhsdb>buildreplayjars [all | boot | app] + create files: + all: + app.jar, boot.jar + boot: + boot.jar + app: + app.jar + exit SA now. +Second, use the obtained replay text file, replay.txt and jar files, app.jar and boot.jar, using debug version of java + java -Xbootclasspath/p:boot.jar -cp app.jar -XX:ReplayDataFile= -XX:+ReplayCompiles .... + This will replay the compiling process. + + With ReplayCompiles, the replay will recompile all the methods in app.jar, and in boot.jar to emulate the process in java app. + +notes: + 1) Most time, we don't need the boot.jar which is the classes loaded from JDK. It will be only modified when an agent(JVMDI) is running and modifies the classes. + 2) If encounter error as "" not found, that means the SA is using a VMStructs which is different from the one with corefile. In this case, SA has a utility tool vmstructsdump which is located at agent/src/os//proc/ + + Use this tool to dump VM type library: + vmstructsdump libjvm.so > .db + + set env SA_TYPEDB=.db (refer different shell for set envs) diff --git a/agent/doc/clhsdb.html b/agent/doc/clhsdb.html index 838ad556c..f1c43fd1e 100644 --- a/agent/doc/clhsdb.html +++ b/agent/doc/clhsdb.html @@ -37,12 +37,19 @@ Each CLHSDB command can have zero or more arguments and optionally end with outp Available commands: assert true | false turn on/off asserts in SA code attach pid | exec core attach SA to a process or core + buildreplayjars [all | boot | app] build jars for replay, boot.jar for bootclasses, app.jar for application classes class name find a Java class from debuggee and print oop classes print all loaded Java classes with Klass* detach detach SA from current target dis address [ length ] disassemble (sparc/x86) specified number of instructions from given address + dissemble address disassemble nmethod + dumpcfg -a | id Dump the PhaseCFG for every compiler thread that has one live dumpclass { address | name } [ directory ] dump .class file for given Klass* or class name + dumpcodecache dump codecache contents dumpheap [ file ] dump heap in hprof binary format + dumpideal -a | id dump ideal graph like debug flag -XX:+PrintIdeal + dumpilt -a | id dump inline tree for C2 compilation + dumpreplaydata
| -a | [>replay.txt] dump replay data into a file echo [ true | false ] turn on/off command echo mode examine [ address/count ] | [ address,address] show contents of memory from given address field [ type [ name fieldtype isStatic offset address ] ] print info about a field of HotSpot type @@ -51,29 +58,35 @@ Available commands: help [ command ] print help message for all commands or just given command history show command history. usual !command-number syntax works. inspect expression inspect a given oop + intConstant [ name [ value ] ] print out hotspot integer constant(s) jdis address show bytecode disassembly of a given Method* jhisto show Java heap histogram jseval script evaluate a given string as JavaScript code jsload file load and evaluate a JavaScript file jstack [-v] show Java stack trace of all Java threads. -v is verbose mode livenmethods show all live nmethods + longConstant [ name [ value ] ] print out hotspot long constant(s)s mem address [ length ] show contents of memory -- also shows closest ELF/COFF symbol if found pmap show Solaris pmap-like output print expression print given Klass*, Method* or arbitrary address printas type expression print given address as given HotSpot type. eg. print JavaThread <address> + printmdo -a | expression print method data oop printstatics [ type ] print static fields of given HotSpot type (or all types if none specified) pstack [-v] show mixed mode stack trace for all Java, non-Java threads. -v is verbose mode quit quit CLHSDB tool reattach detach and re-attach SA to current target + revptrs find liveness of oops scanoops start end [ type ] scan a Oop from given start to end address search [ heap | codecache | threads ] value search a value in heap or codecache or threads source filename load and execute CLHSDB commands from given file symbol name show address of a given ELF/COFF symbol sysprops show all Java System properties + thread id show thread of id threads show all Java threads tokenize ... type [ type [ name super isOop isInteger isUnsigned size ] ] show info. on HotSpot type universe print gc universe + vmstructsdump dump hotspot type library in text verbose true | false turn on/off verbose mode versioncheck [ true | false ] turn on/off debuggee VM version check whatis address print info about any arbitrary address @@ -114,5 +127,11 @@ hsdb> jsload test.js
+

C2 Compilation Replay

+

+When a java process crashes in compiled method, usually a core file is saved. +The C2 replay function can reproduce the compiling process in the core. +c2replay.html + diff --git a/agent/doc/index.html b/agent/doc/index.html index 987e5f9c6..395f10ef6 100644 --- a/agent/doc/index.html +++ b/agent/doc/index.html @@ -220,6 +220,12 @@ These scripts are used to run SA remotely. +

C2 Compilation Replay

+

+When a java process crashes in compiled method, usually a core file is saved. +The C2 replay function can reproduce the compiling process in the core. +c2replay.html +

Debugging transported core dumps

When a core dump is moved from the machine where it was produced to a diff --git a/agent/make/Makefile b/agent/make/Makefile index c0183f17b..a30a99982 100644 --- a/agent/make/Makefile +++ b/agent/make/Makefile @@ -58,10 +58,8 @@ sun.jvm.hotspot.debugger.cdbg.basic \ sun.jvm.hotspot.debugger.cdbg.basic.amd64 \ sun.jvm.hotspot.debugger.cdbg.basic.x86 \ sun.jvm.hotspot.debugger.dummy \ -sun.jvm.hotspot.debugger.ia64 \ sun.jvm.hotspot.debugger.linux \ sun.jvm.hotspot.debugger.linux.amd64 \ -sun.jvm.hotspot.debugger.linux.ia64 \ sun.jvm.hotspot.debugger.linux.x86 \ sun.jvm.hotspot.debugger.posix \ sun.jvm.hotspot.debugger.posix.elf \ @@ -77,7 +75,6 @@ sun.jvm.hotspot.debugger.sparc \ sun.jvm.hotspot.debugger.win32.coff \ sun.jvm.hotspot.debugger.windbg \ sun.jvm.hotspot.debugger.windbg.amd64 \ -sun.jvm.hotspot.debugger.windbg.ia64 \ sun.jvm.hotspot.debugger.windbg.x86 \ sun.jvm.hotspot.debugger.x86 \ sun.jvm.hotspot.gc_implementation \ @@ -97,10 +94,8 @@ sun.jvm.hotspot.runtime.amd64 \ sun.jvm.hotspot.runtime.bsd \ sun.jvm.hotspot.runtime.bsd_amd64 \ sun.jvm.hotspot.runtime.bsd_x86 \ -sun.jvm.hotspot.runtime.ia64 \ sun.jvm.hotspot.runtime.linux \ sun.jvm.hotspot.runtime.linux_amd64 \ -sun.jvm.hotspot.runtime.linux_ia64 \ sun.jvm.hotspot.runtime.linux_sparc \ sun.jvm.hotspot.runtime.linux_x86 \ sun.jvm.hotspot.runtime.posix \ @@ -109,7 +104,6 @@ sun.jvm.hotspot.runtime.solaris_sparc \ sun.jvm.hotspot.runtime.solaris_x86 \ sun.jvm.hotspot.runtime.sparc \ sun.jvm.hotspot.runtime.win32_amd64 \ -sun.jvm.hotspot.runtime.win32_ia64 \ sun.jvm.hotspot.runtime.win32_x86 \ sun.jvm.hotspot.runtime.x86 \ sun.jvm.hotspot.tools \ @@ -152,7 +146,6 @@ sun/jvm/hotspot/debugger/cdbg/basic/*.java \ sun/jvm/hotspot/debugger/cdbg/basic/amd64/*.java \ sun/jvm/hotspot/debugger/cdbg/basic/x86/*.java \ sun/jvm/hotspot/debugger/dummy/*.java \ -sun/jvm/hotspot/debugger/ia64/*.java \ sun/jvm/hotspot/debugger/linux/*.java \ sun/jvm/hotspot/debugger/linux/x86/*.java \ sun/jvm/hotspot/debugger/posix/*.java \ @@ -168,7 +161,6 @@ sun/jvm/hotspot/debugger/remote/x86/*.java \ sun/jvm/hotspot/debugger/sparc/*.java \ sun/jvm/hotspot/debugger/win32/coff/*.java \ sun/jvm/hotspot/debugger/windbg/*.java \ -sun/jvm/hotspot/debugger/windbg/ia64/*.java \ sun/jvm/hotspot/debugger/windbg/x86/*.java \ sun/jvm/hotspot/debugger/x86/*.java \ sun/jvm/hotspot/gc_implementation/g1/*.java \ @@ -186,10 +178,8 @@ sun/jvm/hotspot/runtime/amd64/*.java \ sun/jvm/hotspot/runtime/bsd/*.java \ sun/jvm/hotspot/runtime/bsd_amd64/*.java \ sun/jvm/hotspot/runtime/bsd_x86/*.java \ -sun/jvm/hotspot/runtime/ia64/*.java \ sun/jvm/hotspot/runtime/linux/*.java \ sun/jvm/hotspot/runtime/linux_amd64/*.java \ -sun/jvm/hotspot/runtime/linux_ia64/*.java \ sun/jvm/hotspot/runtime/linux_sparc/*.java \ sun/jvm/hotspot/runtime/linux_x86/*.java \ sun/jvm/hotspot/runtime/posix/*.java \ @@ -198,7 +188,6 @@ sun/jvm/hotspot/runtime/solaris_sparc/*.java \ sun/jvm/hotspot/runtime/solaris_x86/*.java \ sun/jvm/hotspot/runtime/sparc/*.java \ sun/jvm/hotspot/runtime/win32_amd64/*.java \ -sun/jvm/hotspot/runtime/win32_ia64/*.java \ sun/jvm/hotspot/runtime/win32_x86/*.java \ sun/jvm/hotspot/runtime/x86/*.java \ sun/jvm/hotspot/tools/*.java \ @@ -258,6 +247,7 @@ SA_BUILD_VERSION_PROP = "sun.jvm.hotspot.runtime.VM.saBuildVersion=$(SA_BUILD_VE SA_PROPERTIES = $(OUTPUT_DIR)/sa.properties JAVAC = $(JDK_HOME)/bin/javac +JAVA = $(JDK_HOME)/bin/java JAVADOC = $(JDK_HOME)/bin/javadoc RMIC = $(JDK_HOME)/bin/rmic @@ -298,7 +288,7 @@ filelist: $(ALLFILES) .PHONY: natives natives: - cd ../src/os/`java -classpath $(OUTPUT_DIR) sun.jvm.hotspot.utilities.PlatformInfo`; $(MAKE) all + cd ../src/os/`$(JAVA) -classpath $(OUTPUT_DIR) sun.jvm.hotspot.utilities.PlatformInfo`; $(MAKE) all .PHONY: sa-jdi.jar sa-jdi.jar: @@ -323,5 +313,5 @@ sa.jar: clean:: rm -rf filelist - cd ../src/os/`java -classpath $(OUTPUT_DIR) sun.jvm.hotspot.utilities.PlatformInfo`; $(MAKE) clean + cd ../src/os/`$(JAVA) -classpath $(OUTPUT_DIR) sun.jvm.hotspot.utilities.PlatformInfo`; $(MAKE) clean rm -rf $(BUILD_DIR)/* diff --git a/agent/src/share/classes/sun/jvm/hotspot/CommandProcessor.java b/agent/src/share/classes/sun/jvm/hotspot/CommandProcessor.java index c640e4ecc..23058f428 100644 --- a/agent/src/share/classes/sun/jvm/hotspot/CommandProcessor.java +++ b/agent/src/share/classes/sun/jvm/hotspot/CommandProcessor.java @@ -33,6 +33,7 @@ import sun.jvm.hotspot.types.Type; import sun.jvm.hotspot.types.Field; import sun.jvm.hotspot.HotSpotTypeDataBase; import sun.jvm.hotspot.types.basic.BasicType; +import sun.jvm.hotspot.types.basic.BasicTypeDataBase; import sun.jvm.hotspot.types.CIntegerType; import sun.jvm.hotspot.code.*; import sun.jvm.hotspot.compiler.*; @@ -448,6 +449,112 @@ public class CommandProcessor { } } }, + new Command("dumpreplaydata", "dumpreplaydata {

| -a | }", false) { + // This is used to dump replay data from ciInstanceKlass, ciMethodData etc + // default file name is replay.txt, also if java crashes in compiler + // thread, this file will be dumped in error processing. + public void doit(Tokens t) { + if (t.countTokens() != 1) { + usage(); + return; + } + String name = t.nextToken(); + Address a = null; + try { + a = VM.getVM().getDebugger().parseAddress(name); + } catch (NumberFormatException e) { } + if (a != null) { + // only nmethod, Method, MethodData and InstanceKlass needed to + // dump replay data + + CodeBlob cb = VM.getVM().getCodeCache().findBlob(a); + if (cb != null && (cb instanceof NMethod)) { + ((NMethod)cb).dumpReplayData(out); + return; + } + // assume it is Metadata + Metadata meta = Metadata.instantiateWrapperFor(a); + if (meta != null) { + meta.dumpReplayData(out); + } else { + usage(); + return; + } + } + // Not an address + boolean all = name.equals("-a"); + Threads threads = VM.getVM().getThreads(); + for (JavaThread thread = threads.first(); thread != null; thread = thread.next()) { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + thread.printThreadIDOn(new PrintStream(bos)); + if (all || bos.toString().equals(name)) { + if (thread instanceof CompilerThread) { + CompilerThread ct = (CompilerThread)thread; + ciEnv env = ct.env(); + if (env != null) { + env.dumpReplayData(out); + } + } + } + } + } + }, + new Command("buildreplayjars", "buildreplayjars [ all | app | boot ] | [ prefix ]", false) { + // This is used to dump jar files of all the classes + // loaded in the core. Everything on the bootclasspath + // will go in boot.jar and everything else will go in + // app.jar. Then the classes can be loaded by the replay + // jvm using -Xbootclasspath/p:boot.jar -cp app.jar. boot.jar usually + // not needed, unless changed by jvmti. + public void doit(Tokens t) { + int tcount = t.countTokens(); + if (tcount > 2) { + usage(); + return; + } + try { + String prefix = ""; + String option = "all"; // default + switch(tcount) { + case 0: + break; + case 1: + option = t.nextToken(); + if (!option.equalsIgnoreCase("all") && !option.equalsIgnoreCase("app") && + !option.equalsIgnoreCase("root")) { + prefix = option; + option = "all"; + } + break; + case 2: + option = t.nextToken(); + prefix = t.nextToken(); + break; + default: + usage(); + return; + } + if (!option.equalsIgnoreCase("all") && !option.equalsIgnoreCase("app") && + !option.equalsIgnoreCase("boot")) { + usage(); + return; + } + ClassDump cd = new ClassDump(); + if (option.equalsIgnoreCase("all") || option.equalsIgnoreCase("boot")) { + cd.setClassFilter(new BootFilter()); + cd.setJarOutput(prefix + "boot.jar"); + cd.run(); + } + if (option.equalsIgnoreCase("all") || option.equalsIgnoreCase("app")) { + cd.setClassFilter(new NonBootFilter()); + cd.setJarOutput(prefix + "app.jar"); + cd.run(); + } + } catch (IOException ioe) { + ioe.printStackTrace(); + } + } + }, new Command("findpc", "findpc address", false) { public void doit(Tokens t) { if (t.countTokens() != 1) { diff --git a/agent/src/share/classes/sun/jvm/hotspot/ci/ciBaseObject.java b/agent/src/share/classes/sun/jvm/hotspot/ci/ciBaseObject.java index fc8826e8d..e69be922f 100644 --- a/agent/src/share/classes/sun/jvm/hotspot/ci/ciBaseObject.java +++ b/agent/src/share/classes/sun/jvm/hotspot/ci/ciBaseObject.java @@ -16,9 +16,9 @@ * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * - * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, - * CA 95054 USA or visit www.sun.com if you need additional information or - * have any questions. + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. * */ @@ -50,4 +50,8 @@ public class ciBaseObject extends VMObject { public ciBaseObject(Address addr) { super(addr); } + + public void dumpReplayData(PrintStream out) { + out.println("# Unknown ci type " + getAddress().getAddressAt(0)); + } } diff --git a/agent/src/share/classes/sun/jvm/hotspot/ci/ciConstant.java b/agent/src/share/classes/sun/jvm/hotspot/ci/ciConstant.java index ab69f4398..0b75eac3e 100644 --- a/agent/src/share/classes/sun/jvm/hotspot/ci/ciConstant.java +++ b/agent/src/share/classes/sun/jvm/hotspot/ci/ciConstant.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -16,9 +16,9 @@ * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * - * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, - * CA 95054 USA or visit www.sun.com if you need additional information or - * have any questions. + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. * */ @@ -60,4 +60,8 @@ public class ciConstant extends VMObject { public ciConstant(Address addr) { super(addr); } + + public void dumpReplayData(PrintStream out) { + // Nothing to be done + } } diff --git a/agent/src/share/classes/sun/jvm/hotspot/ci/ciEnv.java b/agent/src/share/classes/sun/jvm/hotspot/ci/ciEnv.java index eb48eedb3..871ee414a 100644 --- a/agent/src/share/classes/sun/jvm/hotspot/ci/ciEnv.java +++ b/agent/src/share/classes/sun/jvm/hotspot/ci/ciEnv.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -16,9 +16,9 @@ * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * - * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, - * CA 95054 USA or visit www.sun.com if you need additional information or - * have any questions. + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. * */ @@ -74,4 +74,29 @@ public class ciEnv extends VMObject { public CompileTask task() { return new CompileTask(taskField.getValue(this.getAddress())); } + + public void dumpReplayData(PrintStream out) { + out.println("JvmtiExport can_access_local_variables " + + (JvmtiExport.canAccessLocalVariables() ? '1' : '0')); + out.println("JvmtiExport can_hotswap_or_post_breakpoint " + + (JvmtiExport.canHotswapOrPostBreakpoint() ? '1' : '0')); + out.println("JvmtiExport can_post_on_exceptions " + + (JvmtiExport.canPostOnExceptions() ? '1' : '0')); + + GrowableArray objects = factory().objects(); + out.println("# " + objects.length() + " ciObject found"); + for (int i = 0; i < objects.length(); i++) { + ciMetadata o = objects.at(i); + out.println("# ciMetadata" + i + " @ " + o); + o.dumpReplayData(out); + } + CompileTask task = task(); + Method method = task.method(); + int entryBci = task.osrBci(); + Klass holder = method.getMethodHolder(); + out.println("compile " + holder.getName().asString() + " " + + OopUtilities.escapeString(method.getName().asString()) + " " + + method.getSignature().asString() + " " + + entryBci); + } } diff --git a/agent/src/share/classes/sun/jvm/hotspot/ci/ciInstanceKlass.java b/agent/src/share/classes/sun/jvm/hotspot/ci/ciInstanceKlass.java index 4798977ab..0d0a0f637 100644 --- a/agent/src/share/classes/sun/jvm/hotspot/ci/ciInstanceKlass.java +++ b/agent/src/share/classes/sun/jvm/hotspot/ci/ciInstanceKlass.java @@ -16,9 +16,9 @@ * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * - * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, - * CA 95054 USA or visit www.sun.com if you need additional information or - * have any questions. + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. * */ @@ -80,4 +80,84 @@ public class ciInstanceKlass extends ciKlass { public boolean isInitialized() { return initState() == CLASS_STATE_FULLY_INITIALIZED; } + + public void dumpReplayData(PrintStream out) { + InstanceKlass ik = (InstanceKlass)getMetadata(); + ConstantPool cp = ik.getConstants(); + + // Try to record related loaded classes + Klass sub = ik.getSubklassKlass(); + while (sub != null) { + if (sub instanceof InstanceKlass) { + out.println("instanceKlass " + sub.getName().asString()); + } + sub = sub.getNextSiblingKlass(); + } + + final int length = (int) cp.getLength(); + out.print("ciInstanceKlass " + name() + " " + (isLinked() ? 1 : 0) + " " + (isInitialized() ? 1 : 0) + " " + length); + for (int index = 1; index < length; index++) { + out.print(" " + cp.getTags().at(index)); + } + out.println(); + if (isInitialized()) { + Field[] staticFields = ik.getStaticFields(); + for (int i = 0; i < staticFields.length; i++) { + Field f = staticFields[i]; + Oop mirror = ik.getJavaMirror(); + if (f.isFinal() && !f.hasInitialValue()) { + out.print("staticfield " + name() + " " + + OopUtilities.escapeString(f.getID().getName()) + " " + + f.getFieldType().getSignature().asString() + " "); + if (f instanceof ByteField) { + ByteField bf = (ByteField)f; + out.println(bf.getValue(mirror)); + } else if (f instanceof BooleanField) { + BooleanField bf = (BooleanField)f; + out.println(bf.getValue(mirror) ? 1 : 0); + } else if (f instanceof ShortField) { + ShortField bf = (ShortField)f; + out.println(bf.getValue(mirror)); + } else if (f instanceof CharField) { + CharField bf = (CharField)f; + out.println(bf.getValue(mirror) & 0xffff); + } else if (f instanceof IntField) { + IntField bf = (IntField)f; + out.println(bf.getValue(mirror)); + } else if (f instanceof LongField) { + LongField bf = (LongField)f; + out.println(bf.getValue(mirror)); + } else if (f instanceof FloatField) { + FloatField bf = (FloatField)f; + out.println(Float.floatToRawIntBits(bf.getValue(mirror))); + } else if (f instanceof DoubleField) { + DoubleField bf = (DoubleField)f; + out.println(Double.doubleToRawLongBits(bf.getValue(mirror))); + } else if (f instanceof OopField) { + OopField bf = (OopField)f; + Oop value = bf.getValue(mirror); + if (value == null) { + out.println("null"); + } else if (value.isInstance()) { + Instance inst = (Instance)value; + if (inst.isA(SystemDictionary.getStringKlass())) { + out.println("\"" + OopUtilities.stringOopToEscapedString(inst) + "\""); + } else { + out.println(inst.getKlass().getName().asString()); + } + } else if (value.isObjArray()) { + ObjArray oa = (ObjArray)value; + Klass ek = (ObjArrayKlass)oa.getKlass(); + out.println(oa.getLength() + " " + ek.getName().asString()); + } else if (value.isTypeArray()) { + TypeArray ta = (TypeArray)value; + out.println(ta.getLength()); + } else { + out.println(value); + } + } + } + } + } + } } diff --git a/agent/src/share/classes/sun/jvm/hotspot/ci/ciMethod.java b/agent/src/share/classes/sun/jvm/hotspot/ci/ciMethod.java index 45d43cd0d..3468b0d65 100644 --- a/agent/src/share/classes/sun/jvm/hotspot/ci/ciMethod.java +++ b/agent/src/share/classes/sun/jvm/hotspot/ci/ciMethod.java @@ -16,9 +16,9 @@ * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * - * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, - * CA 95054 USA or visit www.sun.com if you need additional information or - * have any questions. + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. * */ @@ -88,4 +88,19 @@ public class ciMethod extends ciMetadata { st.printf(" %s::%s", method.getMethodHolder().getName().asString().replace('/', '.'), method.getName().asString()); } + + public void dumpReplayData(PrintStream out) { + Method method = (Method)getMetadata(); + NMethod nm = method.getNativeMethod(); + Klass holder = method.getMethodHolder(); + out.println("ciMethod " + + holder.getName().asString() + " " + + OopUtilities.escapeString(method.getName().asString()) + " " + + method.getSignature().asString() + " " + + method.getInvocationCounter() + " " + + method.getBackedgeCounter() + " " + + interpreterInvocationCount() + " " + + interpreterThrowoutCount() + " " + + instructionsSize()); + } } diff --git a/agent/src/share/classes/sun/jvm/hotspot/ci/ciMethodData.java b/agent/src/share/classes/sun/jvm/hotspot/ci/ciMethodData.java index 0399e2751..117a9488c 100644 --- a/agent/src/share/classes/sun/jvm/hotspot/ci/ciMethodData.java +++ b/agent/src/share/classes/sun/jvm/hotspot/ci/ciMethodData.java @@ -16,9 +16,9 @@ * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * - * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, - * CA 95054 USA or visit www.sun.com if you need additional information or - * have any questions. + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. * */ @@ -174,4 +174,52 @@ public class ciMethodData extends ciMetadata { } } + public void dumpReplayData(PrintStream out) { + MethodData mdo = (MethodData)getMetadata(); + Method method = mdo.getMethod(); + Klass holder = method.getMethodHolder(); + out.print("ciMethodData " + + holder.getName().asString() + " " + + OopUtilities.escapeString(method.getName().asString()) + " " + + method.getSignature().asString() + " " + + state() + " " + currentMileage()); + byte[] orig = orig(); + out.print(" orig " + orig.length); + for (int i = 0; i < orig.length; i++) { + out.print(" " + (orig[i] & 0xff)); + } + + long[] data = data(); + out.print(" data " + data.length); + for (int i = 0; i < data.length; i++) { + out.print(" 0x" + Long.toHexString(data[i])); + } + int count = 0; + for (int round = 0; round < 2; round++) { + if (round == 1) out.print(" oops " + count); + ProfileData pdata = firstData(); + for ( ; isValid(pdata); pdata = nextData(pdata)) { + if (pdata instanceof ciReceiverTypeData) { + ciReceiverTypeData vdata = (ciReceiverTypeData)pdata; + for (int i = 0; i < vdata.rowLimit(); i++) { + ciKlass k = vdata.receiverAt(i); + if (k != null) { + if (round == 0) count++; + else out.print(" " + ((vdata.dp() + vdata.cellOffset(vdata.receiverCellIndex(i))) / MethodData.cellSize) + " " + k.name()); + } + } + } else if (pdata instanceof ciVirtualCallData) { + ciVirtualCallData vdata = (ciVirtualCallData)pdata; + for (int i = 0; i < vdata.rowLimit(); i++) { + ciKlass k = vdata.receiverAt(i); + if (k != null) { + if (round == 0) count++; + else out.print(" " + ((vdata.dp() + vdata.cellOffset(vdata.receiverCellIndex(i))) / MethodData.cellSize + " " + k.name())); + } + } + } + } + } + out.println(); + } } diff --git a/agent/src/share/classes/sun/jvm/hotspot/code/NMethod.java b/agent/src/share/classes/sun/jvm/hotspot/code/NMethod.java index 839ad00aa..b0b370cef 100644 --- a/agent/src/share/classes/sun/jvm/hotspot/code/NMethod.java +++ b/agent/src/share/classes/sun/jvm/hotspot/code/NMethod.java @@ -498,6 +498,42 @@ public class NMethod extends CodeBlob { method.getSignature().asString(); } + public void dumpReplayData(PrintStream out) { + HashMap h = new HashMap(); + for (int i = 1; i < getMetadataLength(); i++) { + Metadata meta = Metadata.instantiateWrapperFor(getMetadataAt(i)); + System.err.println(meta); + if (h.get(meta) != null) continue; + h.put(meta, meta); + if (meta instanceof InstanceKlass) { + ((InstanceKlass)meta).dumpReplayData(out); + } else if (meta instanceof Method) { + ((Method)meta).dumpReplayData(out); + MethodData mdo = ((Method)meta).getMethodData(); + if (mdo != null) { + mdo.dumpReplayData(out); + } + } + } + Method method = getMethod(); + if (h.get(method) == null) { + method.dumpReplayData(out); + MethodData mdo = method.getMethodData(); + if (mdo != null) { + mdo.dumpReplayData(out); + } + } + if (h.get(method.getMethodHolder()) == null) { + ((InstanceKlass)method.getMethodHolder()).dumpReplayData(out); + } + Klass holder = method.getMethodHolder(); + out.println("compile " + holder.getName().asString() + " " + + OopUtilities.escapeString(method.getName().asString()) + " " + + method.getSignature().asString() + " " + + getEntryBCI()); + + } + //-------------------------------------------------------------------------------- // Internals only below this point // diff --git a/agent/src/share/classes/sun/jvm/hotspot/compiler/CompileTask.java b/agent/src/share/classes/sun/jvm/hotspot/compiler/CompileTask.java index deca63123..45ef0a2d2 100644 --- a/agent/src/share/classes/sun/jvm/hotspot/compiler/CompileTask.java +++ b/agent/src/share/classes/sun/jvm/hotspot/compiler/CompileTask.java @@ -56,7 +56,7 @@ public class CompileTask extends VMObject { } public Method method() { - Address oh = methodField.getValue(getAddress()).getAddressAt(0); + Address oh = methodField.getValue(getAddress()); return (Method)Metadata.instantiateWrapperFor(oh); } diff --git a/agent/src/share/classes/sun/jvm/hotspot/oops/ConstantPoolCache.java b/agent/src/share/classes/sun/jvm/hotspot/oops/ConstantPoolCache.java index 7b7d0c3ad..b238cd309 100644 --- a/agent/src/share/classes/sun/jvm/hotspot/oops/ConstantPoolCache.java +++ b/agent/src/share/classes/sun/jvm/hotspot/oops/ConstantPoolCache.java @@ -86,7 +86,7 @@ public class ConstantPoolCache extends Metadata { public void printValueOn(PrintStream tty) { - tty.print("ConstantPoolCache for " + getConstants().getPoolHolder().getName().asString()); + tty.print("ConstantPoolCache for " + getConstants().getPoolHolder().getName().asString() + " address = " + getAddress() + " offset = " + baseOffset); } public int getLength() { diff --git a/agent/src/share/classes/sun/jvm/hotspot/oops/Field.java b/agent/src/share/classes/sun/jvm/hotspot/oops/Field.java index 665315361..621c8cf4b 100644 --- a/agent/src/share/classes/sun/jvm/hotspot/oops/Field.java +++ b/agent/src/share/classes/sun/jvm/hotspot/oops/Field.java @@ -110,6 +110,8 @@ public class Field { public Symbol getSignature() { return signature; } public Symbol getGenericSignature() { return genericSignature; } + public boolean hasInitialValue() { return holder.getFieldInitialValueIndex(fieldIndex) != 0; } + // // Following acccessors are for named, non-VM fields only // diff --git a/agent/src/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java b/agent/src/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java index a2c7afb60..ba07fa348 100644 --- a/agent/src/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java +++ b/agent/src/share/classes/sun/jvm/hotspot/oops/InstanceKlass.java @@ -278,7 +278,7 @@ public class InstanceKlass extends Klass { } public short getFieldGenericSignatureIndex(int index) { - int len = getFields().length(); + // int len = getFields().length(); int allFieldsCount = getAllFieldsCount(); int generic_signature_slot = allFieldsCount * FIELD_SLOTS; for (int i = 0; i < allFieldsCount; i++) { @@ -325,7 +325,7 @@ public class InstanceKlass extends Klass { public KlassArray getTransitiveInterfaces() { return new KlassArray(transitiveInterfaces.getValue(getAddress())); } public int getJavaFieldsCount() { return (int) javaFieldsCount.getValue(this); } public int getAllFieldsCount() { - int len = getFields().length(); + int len = getFields().length(); int allFieldsCount = 0; for (; allFieldsCount*FIELD_SLOTS < len; allFieldsCount++) { short flags = getFieldAccessFlags(allFieldsCount); @@ -581,6 +581,19 @@ public class InstanceKlass extends Klass { } } + public Field[] getStaticFields() { + U2Array fields = getFields(); + int length = getJavaFieldsCount(); + ArrayList result = new ArrayList(); + for (int index = 0; index < length; index++) { + Field f = newField(index); + if (f.isStatic()) { + result.add(f); + } + } + return (Field[])result.toArray(new Field[result.size()]); + } + public void iterateNonStaticFields(OopVisitor visitor, Oop obj) { if (getSuper() != null) { ((InstanceKlass) getSuper()).iterateNonStaticFields(visitor, obj); @@ -979,4 +992,84 @@ public class InstanceKlass extends Klass { } return -1; } + + public void dumpReplayData(PrintStream out) { + ConstantPool cp = getConstants(); + + // Try to record related loaded classes + Klass sub = getSubklassKlass(); + while (sub != null) { + if (sub instanceof InstanceKlass) { + out.println("instanceKlass " + sub.getName().asString()); + } + sub = sub.getNextSiblingKlass(); + } + + final int length = (int) cp.getLength(); + out.print("ciInstanceKlass " + getName().asString() + " " + (isLinked() ? 1 : 0) + " " + (isInitialized() ? 1 : 0) + " " + length); + for (int index = 1; index < length; index++) { + out.print(" " + cp.getTags().at(index)); + } + out.println(); + if (isInitialized()) { + Field[] staticFields = getStaticFields(); + for (int i = 0; i < staticFields.length; i++) { + Field f = staticFields[i]; + Oop mirror = getJavaMirror(); + if (f.isFinal() && !f.hasInitialValue()) { + out.print("staticfield " + getName().asString() + " " + + OopUtilities.escapeString(f.getID().getName()) + " " + + f.getFieldType().getSignature().asString() + " "); + if (f instanceof ByteField) { + ByteField bf = (ByteField)f; + out.println(bf.getValue(mirror)); + } else if (f instanceof BooleanField) { + BooleanField bf = (BooleanField)f; + out.println(bf.getValue(mirror) ? 1 : 0); + } else if (f instanceof ShortField) { + ShortField bf = (ShortField)f; + out.println(bf.getValue(mirror)); + } else if (f instanceof CharField) { + CharField bf = (CharField)f; + out.println(bf.getValue(mirror) & 0xffff); + } else if (f instanceof IntField) { + IntField bf = (IntField)f; + out.println(bf.getValue(mirror)); + } else if (f instanceof LongField) { + LongField bf = (LongField)f; + out.println(bf.getValue(mirror)); + } else if (f instanceof FloatField) { + FloatField bf = (FloatField)f; + out.println(Float.floatToRawIntBits(bf.getValue(mirror))); + } else if (f instanceof DoubleField) { + DoubleField bf = (DoubleField)f; + out.println(Double.doubleToRawLongBits(bf.getValue(mirror))); + } else if (f instanceof OopField) { + OopField bf = (OopField)f; + + Oop value = bf.getValue(mirror); + if (value == null) { + out.println("null"); + } else if (value.isInstance()) { + Instance inst = (Instance)value; + if (inst.isA(SystemDictionary.getStringKlass())) { + out.println("\"" + OopUtilities.stringOopToEscapedString(inst) + "\""); + } else { + out.println(inst.getKlass().getName().asString()); + } + } else if (value.isObjArray()) { + ObjArray oa = (ObjArray)value; + Klass ek = (ObjArrayKlass)oa.getKlass(); + out.println(oa.getLength() + " " + ek.getName().asString()); + } else if (value.isTypeArray()) { + TypeArray ta = (TypeArray)value; + out.println(ta.getLength()); + } else { + out.println(value); + } + } + } + } + } + } } diff --git a/agent/src/share/classes/sun/jvm/hotspot/oops/Metadata.java b/agent/src/share/classes/sun/jvm/hotspot/oops/Metadata.java index 35ce375b5..4fc2ed8c6 100644 --- a/agent/src/share/classes/sun/jvm/hotspot/oops/Metadata.java +++ b/agent/src/share/classes/sun/jvm/hotspot/oops/Metadata.java @@ -79,4 +79,7 @@ abstract public class Metadata extends VMObject { } abstract public void printValueOn(PrintStream tty); + public void dumpReplayData(PrintStream out) { + out.println("# Unknown Metadata"); + } } diff --git a/agent/src/share/classes/sun/jvm/hotspot/oops/Method.java b/agent/src/share/classes/sun/jvm/hotspot/oops/Method.java index 449c0695b..2fd57fd97 100644 --- a/agent/src/share/classes/sun/jvm/hotspot/oops/Method.java +++ b/agent/src/share/classes/sun/jvm/hotspot/oops/Method.java @@ -358,6 +358,25 @@ public class Method extends Metadata { buf.append(")"); return buf.toString().replace('/', '.'); } + + public void dumpReplayData(PrintStream out) { + NMethod nm = getNativeMethod(); + int code_size = 0; + if (nm != null) { + code_size = (int)nm.codeEnd().minus(nm.getVerifiedEntryPoint()); + } + Klass holder = getMethodHolder(); + out.println("ciMethod " + + holder.getName().asString() + " " + + OopUtilities.escapeString(getName().asString()) + " " + + getSignature().asString() + " " + + getInvocationCounter() + " " + + getBackedgeCounter() + " " + + interpreterInvocationCount() + " " + + interpreterThrowoutCount() + " " + + code_size); + } + public int interpreterThrowoutCount() { return (int) interpreterThrowoutCountField.getValue(this); } diff --git a/agent/src/share/classes/sun/jvm/hotspot/oops/MethodData.java b/agent/src/share/classes/sun/jvm/hotspot/oops/MethodData.java index e6d233f14..40ede7277 100644 --- a/agent/src/share/classes/sun/jvm/hotspot/oops/MethodData.java +++ b/agent/src/share/classes/sun/jvm/hotspot/oops/MethodData.java @@ -332,4 +332,59 @@ public class MethodData extends Metadata { public int currentMileage() { return 20000; } + + public void dumpReplayData(PrintStream out) { + Method method = getMethod(); + Klass holder = method.getMethodHolder(); + out.print("ciMethodData " + + holder.getName().asString() + " " + + OopUtilities.escapeString(method.getName().asString()) + " " + + method.getSignature().asString() + " " + + "2" + " " + + currentMileage()); + byte[] orig = orig(); + out.print(" orig " + orig.length); + for (int i = 0; i < orig.length; i++) { + out.print(" " + (orig[i] & 0xff)); + } + + long[] data = data(); + out.print(" data " + data.length); + for (int i = 0; i < data.length; i++) { + out.print(" 0x" + Long.toHexString(data[i])); + } + int count = 0; + for (int round = 0; round < 2; round++) { + if (round == 1) out.print(" oops " + count); + ProfileData pdata = firstData(); + for ( ; isValid(pdata); pdata = nextData(pdata)) { + if (pdata instanceof ReceiverTypeData) { + ReceiverTypeData vdata = (ReceiverTypeData)pdata; + for (int i = 0; i < vdata.rowLimit(); i++) { + Klass k = vdata.receiver(i); + if (k != null) { + if (round == 0) count++; + else out.print(" " + + (dpToDi(vdata.dp() + + vdata.cellOffset(vdata.receiverCellIndex(i))) / cellSize) + " " + + k.getName().asString()); + } + } + } else if (pdata instanceof VirtualCallData) { + VirtualCallData vdata = (VirtualCallData)pdata; + for (int i = 0; i < vdata.rowLimit(); i++) { + Klass k = vdata.receiver(i); + if (k != null) { + if (round == 0) count++; + else out.print(" " + + (dpToDi(vdata.dp() + + vdata.cellOffset(vdata.receiverCellIndex(i))) / cellSize) + " " + + k.getName().asString()); + } + } + } + } + } + out.println(); + } } diff --git a/src/share/vm/ci/ciClassList.hpp b/src/share/vm/ci/ciClassList.hpp index 7272ea875..71e5dd18c 100644 --- a/src/share/vm/ci/ciClassList.hpp +++ b/src/share/vm/ci/ciClassList.hpp @@ -106,6 +106,7 @@ friend class ciSymbol; \ friend class ciArray; \ friend class ciObjArray; \ friend class ciMetadata; \ +friend class ciReplay; \ friend class ciTypeArray; \ friend class ciType; \ friend class ciReturnAddress; \ diff --git a/src/share/vm/ci/ciEnv.cpp b/src/share/vm/ci/ciEnv.cpp index 0465b9cf9..878313d11 100644 --- a/src/share/vm/ci/ciEnv.cpp +++ b/src/share/vm/ci/ciEnv.cpp @@ -30,6 +30,7 @@ #include "ci/ciInstanceKlass.hpp" #include "ci/ciMethod.hpp" #include "ci/ciNullObject.hpp" +#include "ci/ciReplay.hpp" #include "ci/ciUtilities.hpp" #include "classfile/systemDictionary.hpp" #include "classfile/vmSymbols.hpp" @@ -772,6 +773,11 @@ ciMethod* ciEnv::get_method_by_index_impl(constantPoolHandle cpool, : !m->method_holder()->is_loaded())) { m = NULL; } +#ifdef ASSERT + if (m != NULL && ReplayCompiles && !ciReplay::is_loaded(m)) { + m = NULL; + } +#endif if (m != NULL) { // We found the method. return get_method(m); @@ -1144,3 +1150,43 @@ void ciEnv::record_out_of_memory_failure() { // If memory is low, we stop compiling methods. record_method_not_compilable("out of memory"); } + +fileStream* ciEnv::_replay_data_stream = NULL; + +void ciEnv::dump_replay_data() { + VM_ENTRY_MARK; + MutexLocker ml(Compile_lock); + if (_replay_data_stream == NULL) { + _replay_data_stream = new (ResourceObj::C_HEAP, mtCompiler) fileStream(ReplayDataFile); + if (_replay_data_stream == NULL) { + fatal(err_msg("Can't open %s for replay data", ReplayDataFile)); + } + } + dump_replay_data(_replay_data_stream); +} + + +void ciEnv::dump_replay_data(outputStream* out) { + ASSERT_IN_VM; + +#if INCLUDE_JVMTI + out->print_cr("JvmtiExport can_access_local_variables %d", _jvmti_can_access_local_variables); + out->print_cr("JvmtiExport can_hotswap_or_post_breakpoint %d", _jvmti_can_hotswap_or_post_breakpoint); + out->print_cr("JvmtiExport can_post_on_exceptions %d", _jvmti_can_post_on_exceptions); +#endif // INCLUDE_JVMTI + + GrowableArray* objects = _factory->get_ci_metadata(); + out->print_cr("# %d ciObject found", objects->length()); + for (int i = 0; i < objects->length(); i++) { + objects->at(i)->dump_replay_data(out); + } + Method* method = task()->method(); + int entry_bci = task()->osr_bci(); + // Klass holder = method->method_holder(); + out->print_cr("compile %s %s %s %d", + method->klass_name()->as_quoted_ascii(), + method->name()->as_quoted_ascii(), + method->signature()->as_quoted_ascii(), + entry_bci); + out->flush(); +} diff --git a/src/share/vm/ci/ciEnv.hpp b/src/share/vm/ci/ciEnv.hpp index f252dc7c0..042bc32fa 100644 --- a/src/share/vm/ci/ciEnv.hpp +++ b/src/share/vm/ci/ciEnv.hpp @@ -46,6 +46,8 @@ class ciEnv : StackObj { friend class CompileBroker; friend class Dependencies; // for get_object, during logging + static fileStream* _replay_data_stream; + private: Arena* _arena; // Alias for _ciEnv_arena except in init_shared_objects() Arena _ciEnv_arena; @@ -448,6 +450,13 @@ public: // RedefineClasses support void metadata_do(void f(Metadata*)) { _factory->metadata_do(f); } + + // Dump the compilation replay data for this ciEnv to + // ReplayDataFile, creating the file if needed. + void dump_replay_data(); + + // Dump the compilation replay data for the ciEnv to the stream. + void dump_replay_data(outputStream* out); }; #endif // SHARE_VM_CI_CIENV_HPP diff --git a/src/share/vm/ci/ciInstanceKlass.cpp b/src/share/vm/ci/ciInstanceKlass.cpp index 7a3eb328f..de5e973bf 100644 --- a/src/share/vm/ci/ciInstanceKlass.cpp +++ b/src/share/vm/ci/ciInstanceKlass.cpp @@ -561,3 +561,114 @@ ciInstanceKlass* ciInstanceKlass::implementor() { } return impl; } + +// Utility class for printing of the contents of the static fields for +// use by compilation replay. It only prints out the information that +// could be consumed by the compiler, so for primitive types it prints +// out the actual value. For Strings it's the actual string value. +// For array types it it's first level array size since that's the +// only value which statically unchangeable. For all other reference +// types it simply prints out the dynamic type. + +class StaticFinalFieldPrinter : public FieldClosure { + outputStream* _out; + const char* _holder; + public: + StaticFinalFieldPrinter(outputStream* out, const char* holder) : + _out(out), + _holder(holder) { + } + void do_field(fieldDescriptor* fd) { + if (fd->is_final() && !fd->has_initial_value()) { + oop mirror = fd->field_holder()->java_mirror(); + _out->print("staticfield %s %s %s ", _holder, fd->name()->as_quoted_ascii(), fd->signature()->as_quoted_ascii()); + switch (fd->field_type()) { + case T_BYTE: _out->print_cr("%d", mirror->byte_field(fd->offset())); break; + case T_BOOLEAN: _out->print_cr("%d", mirror->bool_field(fd->offset())); break; + case T_SHORT: _out->print_cr("%d", mirror->short_field(fd->offset())); break; + case T_CHAR: _out->print_cr("%d", mirror->char_field(fd->offset())); break; + case T_INT: _out->print_cr("%d", mirror->int_field(fd->offset())); break; + case T_LONG: _out->print_cr(INT64_FORMAT, mirror->long_field(fd->offset())); break; + case T_FLOAT: { + float f = mirror->float_field(fd->offset()); + _out->print_cr("%d", *(int*)&f); + break; + } + case T_DOUBLE: { + double d = mirror->double_field(fd->offset()); + _out->print_cr(INT64_FORMAT, *(jlong*)&d); + break; + } + case T_ARRAY: { + oop value = mirror->obj_field_acquire(fd->offset()); + if (value == NULL) { + _out->print_cr("null"); + } else { + typeArrayOop ta = (typeArrayOop)value; + _out->print("%d", ta->length()); + if (value->is_objArray()) { + objArrayOop oa = (objArrayOop)value; + const char* klass_name = value->klass()->name()->as_quoted_ascii(); + _out->print(" %s", klass_name); + } + _out->cr(); + } + break; + } + case T_OBJECT: { + oop value = mirror->obj_field_acquire(fd->offset()); + if (value == NULL) { + _out->print_cr("null"); + } else if (value->is_instance()) { + if (value->is_a(SystemDictionary::String_klass())) { + _out->print("\""); + _out->print_raw(java_lang_String::as_quoted_ascii(value)); + _out->print_cr("\""); + } else { + const char* klass_name = value->klass()->name()->as_quoted_ascii(); + _out->print_cr(klass_name); + } + } else { + ShouldNotReachHere(); + } + break; + } + default: + ShouldNotReachHere(); + } + } + } +}; + + +void ciInstanceKlass::dump_replay_data(outputStream* out) { + ASSERT_IN_VM; + InstanceKlass* ik = get_instanceKlass(); + ConstantPool* cp = ik->constants(); + + // Try to record related loaded classes + Klass* sub = ik->subklass(); + while (sub != NULL) { + if (sub->oop_is_instance()) { + out->print_cr("instanceKlass %s", sub->name()->as_quoted_ascii()); + } + sub = sub->next_sibling(); + } + + // Dump out the state of the constant pool tags. During replay the + // tags will be validated for things which shouldn't change and + // classes will be resolved if the tags indicate that they were + // resolved at compile time. + out->print("ciInstanceKlass %s %d %d %d", ik->name()->as_quoted_ascii(), + is_linked(), is_initialized(), cp->length()); + for (int index = 1; index < cp->length(); index++) { + out->print(" %d", cp->tags()->at(index)); + } + out->cr(); + if (is_initialized()) { + // Dump out the static final fields in case the compilation relies + // on their value for correct replay. + StaticFinalFieldPrinter sffp(out, ik->name()->as_quoted_ascii()); + ik->do_local_static_fields(&sffp); + } +} diff --git a/src/share/vm/ci/ciInstanceKlass.hpp b/src/share/vm/ci/ciInstanceKlass.hpp index 205ee493d..f7aeba2df 100644 --- a/src/share/vm/ci/ciInstanceKlass.hpp +++ b/src/share/vm/ci/ciInstanceKlass.hpp @@ -230,6 +230,9 @@ public: // What kind of ciObject is this? bool is_instance_klass() const { return true; } bool is_java_klass() const { return true; } + + // Dump the current state of this klass for compilation replay. + virtual void dump_replay_data(outputStream* out); }; #endif // SHARE_VM_CI_CIINSTANCEKLASS_HPP diff --git a/src/share/vm/ci/ciMetadata.hpp b/src/share/vm/ci/ciMetadata.hpp index cbd0bc028..e0baea966 100644 --- a/src/share/vm/ci/ciMetadata.hpp +++ b/src/share/vm/ci/ciMetadata.hpp @@ -61,6 +61,7 @@ class ciMetadata: public ciBaseObject { virtual bool is_array_klass() const { return false; } virtual bool is_obj_array_klass() const { return false; } virtual bool is_type_array_klass() const { return false; } + virtual void dump_replay_data(outputStream* st) { /* do nothing */ } ciMethod* as_method() { assert(is_method(), "bad cast"); diff --git a/src/share/vm/ci/ciMethod.cpp b/src/share/vm/ci/ciMethod.cpp index 7071f54d0..951183d60 100644 --- a/src/share/vm/ci/ciMethod.cpp +++ b/src/share/vm/ci/ciMethod.cpp @@ -31,6 +31,7 @@ #include "ci/ciMethodData.hpp" #include "ci/ciStreams.hpp" #include "ci/ciSymbol.hpp" +#include "ci/ciReplay.hpp" #include "ci/ciUtilities.hpp" #include "classfile/systemDictionary.hpp" #include "compiler/abstractCompiler.hpp" @@ -139,6 +140,12 @@ ciMethod::ciMethod(methodHandle h_m) : ciMetadata(h_m()) { } if (_interpreter_invocation_count == 0) _interpreter_invocation_count = 1; + _instructions_size = -1; +#ifdef ASSERT + if (ReplayCompiles) { + ciReplay::initialize(this); + } +#endif } @@ -161,7 +168,8 @@ ciMethod::ciMethod(ciInstanceKlass* holder, #if defined(COMPILER2) || defined(SHARK) , _flow( NULL), - _bcea( NULL) + _bcea( NULL), + _instructions_size(-1) #endif // COMPILER2 || SHARK { // Usually holder and accessor are the same type but in some cases @@ -1000,8 +1008,7 @@ bool ciMethod::can_be_osr_compiled(int entry_bci) { // ------------------------------------------------------------------ // ciMethod::has_compiled_code bool ciMethod::has_compiled_code() { - VM_ENTRY_MARK; - return get_Method()->code() != NULL; + return instructions_size() > 0; } int ciMethod::comp_level() { @@ -1039,14 +1046,18 @@ int ciMethod::code_size_for_inlining() { // junk like exception handler, stubs, and constant table, which are // not highly relevant to an inlined method. So we use the more // specific accessor nmethod::insts_size. -int ciMethod::instructions_size(int comp_level) { - GUARDED_VM_ENTRY( - nmethod* code = get_Method()->code(); - if (code != NULL && (comp_level == CompLevel_any || comp_level == code->comp_level())) { - return code->insts_end() - code->verified_entry_point(); - } - return 0; - ) +int ciMethod::instructions_size() { + if (_instructions_size == -1) { + GUARDED_VM_ENTRY( + nmethod* code = get_Method()->code(); + if (code != NULL && (code->comp_level() == CompLevel_full_optimization)) { + _instructions_size = code->insts_end() - code->verified_entry_point(); + } else { + _instructions_size = 0; + } + ); + } + return _instructions_size; } // ------------------------------------------------------------------ @@ -1166,6 +1177,20 @@ ciMethodBlocks *ciMethod::get_method_blocks() { #undef FETCH_FLAG_FROM_VM +void ciMethod::dump_replay_data(outputStream* st) { + ASSERT_IN_VM; + Method* method = get_Method(); + Klass* holder = method->method_holder(); + st->print_cr("ciMethod %s %s %s %d %d %d %d %d", + holder->name()->as_quoted_ascii(), + method->name()->as_quoted_ascii(), + method->signature()->as_quoted_ascii(), + method->invocation_counter()->raw_counter(), + method->backedge_counter()->raw_counter(), + interpreter_invocation_count(), + interpreter_throwout_count(), + _instructions_size); +} // ------------------------------------------------------------------ // ciMethod::print_codes diff --git a/src/share/vm/ci/ciMethod.hpp b/src/share/vm/ci/ciMethod.hpp index 15f652133..ecbaacc45 100644 --- a/src/share/vm/ci/ciMethod.hpp +++ b/src/share/vm/ci/ciMethod.hpp @@ -51,6 +51,7 @@ class ciMethod : public ciMetadata { friend class ciExceptionHandlerStream; friend class ciBytecodeStream; friend class ciMethodHandle; + friend class ciReplay; private: // General method information. @@ -69,6 +70,7 @@ class ciMethod : public ciMetadata { int _handler_count; int _interpreter_invocation_count; int _interpreter_throwout_count; + int _instructions_size; bool _uses_monitors; bool _balanced_monitors; @@ -252,7 +254,6 @@ class ciMethod : public ciMetadata { bool can_be_osr_compiled(int entry_bci); void set_not_compilable(); bool has_compiled_code(); - int instructions_size(int comp_level = CompLevel_any); void log_nmethod_identity(xmlStream* log); bool is_not_reached(int bci); bool was_executed_more_than(int times); @@ -260,6 +261,7 @@ class ciMethod : public ciMetadata { bool is_klass_loaded(int refinfo_index, bool must_be_resolved) const; bool check_call(int refinfo_index, bool is_static) const; bool ensure_method_data(); // make sure it exists in the VM also + int instructions_size(); int scale_count(int count, float prof_factor = 1.); // make MDO count commensurate with IIC // JSR 292 support @@ -291,6 +293,7 @@ class ciMethod : public ciMetadata { bool is_accessor () const; bool is_initializer () const; bool can_be_statically_bound() const { return _can_be_statically_bound; } + void dump_replay_data(outputStream* st); // Print the bytecodes of this method. void print_codes_on(outputStream* st); diff --git a/src/share/vm/ci/ciMethodData.cpp b/src/share/vm/ci/ciMethodData.cpp index aef1c03c7..ee5490be8 100644 --- a/src/share/vm/ci/ciMethodData.cpp +++ b/src/share/vm/ci/ciMethodData.cpp @@ -25,6 +25,7 @@ #include "precompiled.hpp" #include "ci/ciMetadata.hpp" #include "ci/ciMethodData.hpp" +#include "ci/ciReplay.hpp" #include "ci/ciUtilities.hpp" #include "memory/allocation.inline.hpp" #include "memory/resourceArea.hpp" @@ -115,6 +116,11 @@ void ciMethodData::load_data() { _arg_local = mdo->arg_local(); _arg_stack = mdo->arg_stack(); _arg_returned = mdo->arg_returned(); +#ifndef PRODUCT + if (ReplayCompiles) { + ciReplay::initialize(this); + } +#endif } void ciReceiverTypeData::translate_receiver_data_from(ProfileData* data) { @@ -366,6 +372,79 @@ void ciMethodData::print_impl(outputStream* st) { ciMetadata::print_impl(st); } +void ciMethodData::dump_replay_data(outputStream* out) { + ASSERT_IN_VM; + MethodData* mdo = get_MethodData(); + Method* method = mdo->method(); + Klass* holder = method->method_holder(); + out->print("ciMethodData %s %s %s %d %d", + holder->name()->as_quoted_ascii(), + method->name()->as_quoted_ascii(), + method->signature()->as_quoted_ascii(), + _state, + current_mileage()); + + // dump the contents of the MDO header as raw data + unsigned char* orig = (unsigned char*)&_orig; + int length = sizeof(_orig); + out->print(" orig %d", length); + for (int i = 0; i < length; i++) { + out->print(" %d", orig[i]); + } + + // dump the MDO data as raw data + int elements = data_size() / sizeof(intptr_t); + out->print(" data %d", elements); + for (int i = 0; i < elements; i++) { + // We could use INTPTR_FORMAT here but that's a zero justified + // which makes comparing it with the SA version of this output + // harder. +#ifdef _LP64 + out->print(" 0x%" FORMAT64_MODIFIER "x", data()[i]); +#else + out->print(" 0x%x", data()[i]); +#endif + } + + // The MDO contained oop references as ciObjects, so scan for those + // and emit pairs of offset and klass name so that they can be + // reconstructed at runtime. The first round counts the number of + // oop references and the second actually emits them. + int count = 0; + for (int round = 0; round < 2; round++) { + if (round == 1) out->print(" oops %d", count); + ProfileData* pdata = first_data(); + for ( ; is_valid(pdata); pdata = next_data(pdata)) { + if (pdata->is_ReceiverTypeData()) { + ciReceiverTypeData* vdata = (ciReceiverTypeData*)pdata; + for (uint i = 0; i < vdata->row_limit(); i++) { + ciKlass* k = vdata->receiver(i); + if (k != NULL) { + if (round == 0) { + count++; + } else { + out->print(" %d %s", dp_to_di(vdata->dp() + in_bytes(vdata->receiver_offset(i))) / sizeof(intptr_t), k->name()->as_quoted_ascii()); + } + } + } + } else if (pdata->is_VirtualCallData()) { + ciVirtualCallData* vdata = (ciVirtualCallData*)pdata; + for (uint i = 0; i < vdata->row_limit(); i++) { + ciKlass* k = vdata->receiver(i); + if (k != NULL) { + if (round == 0) { + count++; + } else { + out->print(" %d %s", dp_to_di(vdata->dp() + in_bytes(vdata->receiver_offset(i))) / sizeof(intptr_t), k->name()->as_quoted_ascii()); + } + } + } + } + } + } + out->cr(); +} + #ifndef PRODUCT void ciMethodData::print() { print_data_on(tty); diff --git a/src/share/vm/ci/ciMethodData.hpp b/src/share/vm/ci/ciMethodData.hpp index bca5d1cd1..ea650cbd7 100644 --- a/src/share/vm/ci/ciMethodData.hpp +++ b/src/share/vm/ci/ciMethodData.hpp @@ -144,6 +144,7 @@ public: class ciMethodData : public ciMetadata { CI_PACKAGE_ACCESS + friend class ciReplay; private: // Size in bytes @@ -320,6 +321,7 @@ public: void print(); void print_data_on(outputStream* st); #endif + void dump_replay_data(outputStream* out); }; #endif // SHARE_VM_CI_CIMETHODDATA_HPP diff --git a/src/share/vm/ci/ciObject.hpp b/src/share/vm/ci/ciObject.hpp index 6246e173e..c33b79ed5 100644 --- a/src/share/vm/ci/ciObject.hpp +++ b/src/share/vm/ci/ciObject.hpp @@ -131,6 +131,7 @@ public: // Is this a type or value which has no associated class? // It is true of primitive types and null objects. virtual bool is_classless() const { return false; } + virtual void dump_replay_data(outputStream* st) { /* do nothing */ } // Note: some ciObjects refer to oops which have yet to be created. // We refer to these as "unloaded". Specifically, there are diff --git a/src/share/vm/ci/ciObjectFactory.hpp b/src/share/vm/ci/ciObjectFactory.hpp index 10870b904..29de514b2 100644 --- a/src/share/vm/ci/ciObjectFactory.hpp +++ b/src/share/vm/ci/ciObjectFactory.hpp @@ -137,6 +137,7 @@ public: ciReturnAddress* get_return_address(int bci); + GrowableArray* get_ci_metadata() const { return _ci_metadata; } // RedefineClasses support void metadata_do(void f(Metadata*)); diff --git a/src/share/vm/ci/ciReplay.cpp b/src/share/vm/ci/ciReplay.cpp new file mode 100644 index 000000000..726d84b1b --- /dev/null +++ b/src/share/vm/ci/ciReplay.cpp @@ -0,0 +1,942 @@ +/* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#include "precompiled.hpp" +#include "ci/ciMethodData.hpp" +#include "ci/ciReplay.hpp" +#include "ci/ciUtilities.hpp" +#include "compiler/compileBroker.hpp" +#include "memory/allocation.inline.hpp" +#include "memory/oopFactory.hpp" +#include "memory/resourceArea.hpp" +#include "utilities/copy.hpp" + +#ifdef ASSERT + +// ciReplay + +typedef struct _ciMethodDataRecord { + const char* klass; + const char* method; + const char* signature; + int state; + int current_mileage; + intptr_t* data; + int data_length; + char* orig_data; + int orig_data_length; + int oops_length; + jobject* oops_handles; + int* oops_offsets; +} ciMethodDataRecord; + +typedef struct _ciMethodRecord { + const char* klass; + const char* method; + const char* signature; + int instructions_size; + int interpreter_invocation_count; + int interpreter_throwout_count; + int invocation_counter; + int backedge_counter; +} ciMethodRecord; + +class CompileReplay; +static CompileReplay* replay_state; + +class CompileReplay : public StackObj { + private: + FILE* stream; + Thread* thread; + Handle protection_domain; + Handle loader; + + GrowableArray ci_method_records; + GrowableArray ci_method_data_records; + + const char* _error_message; + + char* bufptr; + char* buffer; + int buffer_length; + int buffer_end; + int line_no; + + public: + CompileReplay(const char* filename, TRAPS) { + thread = THREAD; + loader = Handle(thread, SystemDictionary::java_system_loader()); + stream = fopen(filename, "rt"); + if (stream == NULL) { + fprintf(stderr, "Can't open replay file %s\n", filename); + } + buffer_length = 32; + buffer = NEW_RESOURCE_ARRAY(char, buffer_length); + _error_message = NULL; + + test(); + } + + ~CompileReplay() { + if (stream != NULL) fclose(stream); + } + + void test() { + strcpy(buffer, "1 2 foo 4 bar 0x9 \"this is it\""); + bufptr = buffer; + assert(parse_int("test") == 1, "what"); + assert(parse_int("test") == 2, "what"); + assert(strcmp(parse_string(), "foo") == 0, "what"); + assert(parse_int("test") == 4, "what"); + assert(strcmp(parse_string(), "bar") == 0, "what"); + assert(parse_intptr_t("test") == 9, "what"); + assert(strcmp(parse_quoted_string(), "this is it") == 0, "what"); + } + + bool had_error() { + return _error_message != NULL || thread->has_pending_exception(); + } + + bool can_replay() { + return !(stream == NULL || had_error()); + } + + void report_error(const char* msg) { + _error_message = msg; + // Restore the buffer contents for error reporting + for (int i = 0; i < buffer_end; i++) { + if (buffer[i] == '\0') buffer[i] = ' '; + } + } + + int parse_int(const char* label) { + if (had_error()) { + return 0; + } + + int v = 0; + int read; + if (sscanf(bufptr, "%i%n", &v, &read) != 1) { + report_error(label); + } else { + bufptr += read; + } + return v; + } + + intptr_t parse_intptr_t(const char* label) { + if (had_error()) { + return 0; + } + + intptr_t v = 0; + int read; + if (sscanf(bufptr, INTPTR_FORMAT "%n", &v, &read) != 1) { + report_error(label); + } else { + bufptr += read; + } + return v; + } + + void skip_ws() { + // Skip any leading whitespace + while (*bufptr == ' ' || *bufptr == '\t') { + bufptr++; + } + } + + + char* scan_and_terminate(char delim) { + char* str = bufptr; + while (*bufptr != delim && *bufptr != '\0') { + bufptr++; + } + if (*bufptr != '\0') { + *bufptr++ = '\0'; + } + if (bufptr == str) { + // nothing here + return NULL; + } + return str; + } + + char* parse_string() { + if (had_error()) return NULL; + + skip_ws(); + return scan_and_terminate(' '); + } + + char* parse_quoted_string() { + if (had_error()) return NULL; + + skip_ws(); + + if (*bufptr == '"') { + bufptr++; + return scan_and_terminate('"'); + } else { + return scan_and_terminate(' '); + } + } + + const char* parse_escaped_string() { + char* result = parse_quoted_string(); + if (result != NULL) { + unescape_string(result); + } + return result; + } + + // Look for the tag 'tag' followed by an + bool parse_tag_and_count(const char* tag, int& length) { + const char* t = parse_string(); + if (t == NULL) { + return false; + } + + if (strcmp(tag, t) != 0) { + report_error(tag); + return false; + } + length = parse_int("parse_tag_and_count"); + return !had_error(); + } + + // Parse a sequence of raw data encoded as bytes and return the + // resulting data. + char* parse_data(const char* tag, int& length) { + if (!parse_tag_and_count(tag, length)) { + return NULL; + } + + char * result = NEW_RESOURCE_ARRAY(char, length); + for (int i = 0; i < length; i++) { + int val = parse_int("data"); + result[i] = val; + } + return result; + } + + // Parse a standard chunk of data emitted as: + // 'tag' # # ... + // Where each # is an intptr_t item + intptr_t* parse_intptr_data(const char* tag, int& length) { + if (!parse_tag_and_count(tag, length)) { + return NULL; + } + + intptr_t* result = NEW_RESOURCE_ARRAY(intptr_t, length); + for (int i = 0; i < length; i++) { + skip_ws(); + intptr_t val = parse_intptr_t("data"); + result[i] = val; + } + return result; + } + + // Parse a possibly quoted version of a symbol into a symbolOop + Symbol* parse_symbol(TRAPS) { + const char* str = parse_escaped_string(); + if (str != NULL) { + Symbol* sym = SymbolTable::lookup(str, (int)strlen(str), CHECK_NULL); + return sym; + } + return NULL; + } + + // Parse a valid klass name and look it up + Klass* parse_klass(TRAPS) { + const char* str = parse_escaped_string(); + Symbol* klass_name = SymbolTable::lookup(str, (int)strlen(str), CHECK_NULL); + if (klass_name != NULL) { + Klass* k = SystemDictionary::resolve_or_fail(klass_name, loader, protection_domain, true, THREAD); + if (HAS_PENDING_EXCEPTION) { + oop throwable = PENDING_EXCEPTION; + java_lang_Throwable::print(throwable, tty); + tty->cr(); + report_error(str); + return NULL; + } + return k; + } + return NULL; + } + + // Lookup a klass + Klass* resolve_klass(const char* klass, TRAPS) { + Symbol* klass_name = SymbolTable::lookup(klass, (int)strlen(klass), CHECK_NULL); + return SystemDictionary::resolve_or_fail(klass_name, loader, protection_domain, true, CHECK_NULL); + } + + // Parse the standard tuple of + Method* parse_method(TRAPS) { + InstanceKlass* k = (InstanceKlass*)parse_klass(CHECK_NULL); + Symbol* method_name = parse_symbol(CHECK_NULL); + Symbol* method_signature = parse_symbol(CHECK_NULL); + Method* m = k->find_method(method_name, method_signature); + if (m == NULL) { + report_error("can't find method"); + } + return m; + } + + // Process each line of the replay file executing each command until + // the file ends. + void process(TRAPS) { + line_no = 1; + int pos = 0; + int c = getc(stream); + while(c != EOF) { + if (pos + 1 >= buffer_length) { + int newl = buffer_length * 2; + char* newb = NEW_RESOURCE_ARRAY(char, newl); + memcpy(newb, buffer, pos); + buffer = newb; + buffer_length = newl; + } + if (c == '\n') { + // null terminate it, reset the pointer and process the line + buffer[pos] = '\0'; + buffer_end = pos++; + bufptr = buffer; + process_command(CHECK); + if (had_error()) { + tty->print_cr("Error while parsing line %d: %s\n", line_no, _error_message); + tty->print_cr("%s", buffer); + assert(false, "error"); + return; + } + pos = 0; + buffer_end = 0; + line_no++; + } else if (c == '\r') { + // skip LF + } else { + buffer[pos++] = c; + } + c = getc(stream); + } + } + + void process_command(TRAPS) { + char* cmd = parse_string(); + if (cmd == NULL) { + return; + } + if (strcmp("#", cmd) == 0) { + // ignore + } else if (strcmp("compile", cmd) == 0) { + process_compile(CHECK); + } else if (strcmp("ciMethod", cmd) == 0) { + process_ciMethod(CHECK); + } else if (strcmp("ciMethodData", cmd) == 0) { + process_ciMethodData(CHECK); + } else if (strcmp("staticfield", cmd) == 0) { + process_staticfield(CHECK); + } else if (strcmp("ciInstanceKlass", cmd) == 0) { + process_ciInstanceKlass(CHECK); + } else if (strcmp("instanceKlass", cmd) == 0) { + process_instanceKlass(CHECK); +#if INCLUDE_JVMTI + } else if (strcmp("JvmtiExport", cmd) == 0) { + process_JvmtiExport(CHECK); +#endif // INCLUDE_JVMTI + } else { + report_error("unknown command"); + } + } + + // compile + void process_compile(TRAPS) { + // methodHandle method; + Method* method = parse_method(CHECK); + int entry_bci = parse_int("entry_bci"); + Klass* k = method->method_holder(); + ((InstanceKlass*)k)->initialize(THREAD); + if (HAS_PENDING_EXCEPTION) { + oop throwable = PENDING_EXCEPTION; + java_lang_Throwable::print(throwable, tty); + tty->cr(); + if (ReplayIgnoreInitErrors) { + CLEAR_PENDING_EXCEPTION; + ((InstanceKlass*)k)->set_init_state(InstanceKlass::fully_initialized); + } else { + return; + } + } + // Make sure the existence of a prior compile doesn't stop this one + nmethod* nm = (entry_bci != InvocationEntryBci) ? method->lookup_osr_nmethod_for(entry_bci, CompLevel_full_optimization, true) : method->code(); + if (nm != NULL) { + nm->make_not_entrant(); + } + replay_state = this; + CompileBroker::compile_method(method, entry_bci, CompLevel_full_optimization, + methodHandle(), 0, "replay", THREAD); + replay_state = NULL; + reset(); + } + + // ciMethod + // + // + void process_ciMethod(TRAPS) { + Method* method = parse_method(CHECK); + ciMethodRecord* rec = new_ciMethod(method); + rec->invocation_counter = parse_int("invocation_counter"); + rec->backedge_counter = parse_int("backedge_counter"); + rec->interpreter_invocation_count = parse_int("interpreter_invocation_count"); + rec->interpreter_throwout_count = parse_int("interpreter_throwout_count"); + rec->instructions_size = parse_int("instructions_size"); + } + + // ciMethodData orig # # ... data # # ... oops + void process_ciMethodData(TRAPS) { + Method* method = parse_method(CHECK); + /* jsut copied from Method, to build interpret data*/ + if (InstanceRefKlass::owns_pending_list_lock((JavaThread*)THREAD)) { + return; + } + // methodOopDesc::build_interpreter_method_data(method, CHECK); + { + // Grab a lock here to prevent multiple + // MethodData*s from being created. + MutexLocker ml(MethodData_lock, THREAD); + if (method->method_data() == NULL) { + ClassLoaderData* loader_data = method->method_holder()->class_loader_data(); + MethodData* method_data = MethodData::allocate(loader_data, method, CHECK); + method->set_method_data(method_data); + } + } + + // collect and record all the needed information for later + ciMethodDataRecord* rec = new_ciMethodData(method); + rec->state = parse_int("state"); + rec->current_mileage = parse_int("current_mileage"); + + rec->orig_data = parse_data("orig", rec->orig_data_length); + if (rec->orig_data == NULL) { + return; + } + rec->data = parse_intptr_data("data", rec->data_length); + if (rec->data == NULL) { + return; + } + if (!parse_tag_and_count("oops", rec->oops_length)) { + return; + } + rec->oops_handles = NEW_RESOURCE_ARRAY(jobject, rec->oops_length); + rec->oops_offsets = NEW_RESOURCE_ARRAY(int, rec->oops_length); + for (int i = 0; i < rec->oops_length; i++) { + int offset = parse_int("offset"); + if (had_error()) { + return; + } + Klass* k = parse_klass(CHECK); + rec->oops_offsets[i] = offset; + rec->oops_handles[i] = (jobject)(new KlassHandle(THREAD, k)); + } + } + + // instanceKlass + // + // Loads and initializes the klass 'name'. This can be used to + // create particular class loading environments + void process_instanceKlass(TRAPS) { + // just load the referenced class + Klass* k = parse_klass(CHECK); + } + + // ciInstanceKlass tag # # # ... + // + // Load the klass 'name' and link or initialize it. Verify that the + // constant pool is the same length as 'length' and make sure the + // constant pool tags are in the same state. + void process_ciInstanceKlass(TRAPS) { + InstanceKlass* k = (InstanceKlass *)parse_klass(CHECK); + int is_linked = parse_int("is_linked"); + int is_initialized = parse_int("is_initialized"); + int length = parse_int("length"); + if (is_initialized) { + k->initialize(THREAD); + if (HAS_PENDING_EXCEPTION) { + oop throwable = PENDING_EXCEPTION; + java_lang_Throwable::print(throwable, tty); + tty->cr(); + if (ReplayIgnoreInitErrors) { + CLEAR_PENDING_EXCEPTION; + k->set_init_state(InstanceKlass::fully_initialized); + } else { + return; + } + } + } else if (is_linked) { + k->link_class(CHECK); + } + ConstantPool* cp = k->constants(); + if (length != cp->length()) { + report_error("constant pool length mismatch: wrong class files?"); + return; + } + + int parsed_two_word = 0; + for (int i = 1; i < length; i++) { + int tag = parse_int("tag"); + if (had_error()) { + return; + } + switch (cp->tag_at(i).value()) { + case JVM_CONSTANT_UnresolvedClass: { + if (tag == JVM_CONSTANT_Class) { + tty->print_cr("Resolving klass %s at %d", cp->unresolved_klass_at(i)->as_utf8(), i); + Klass* k = cp->klass_at(i, CHECK); + } + break; + } + case JVM_CONSTANT_Long: + case JVM_CONSTANT_Double: + parsed_two_word = i + 1; + + case JVM_CONSTANT_ClassIndex: + case JVM_CONSTANT_StringIndex: + case JVM_CONSTANT_String: + case JVM_CONSTANT_UnresolvedClassInError: + case JVM_CONSTANT_Fieldref: + case JVM_CONSTANT_Methodref: + case JVM_CONSTANT_InterfaceMethodref: + case JVM_CONSTANT_NameAndType: + case JVM_CONSTANT_Utf8: + case JVM_CONSTANT_Integer: + case JVM_CONSTANT_Float: + if (tag != cp->tag_at(i).value()) { + report_error("tag mismatch: wrong class files?"); + return; + } + break; + + case JVM_CONSTANT_Class: + if (tag == JVM_CONSTANT_Class) { + } else if (tag == JVM_CONSTANT_UnresolvedClass) { + tty->print_cr("Warning: entry was unresolved in the replay data"); + } else { + report_error("Unexpected tag"); + return; + } + break; + + case 0: + if (parsed_two_word == i) continue; + + default: + ShouldNotReachHere(); + break; + } + + } + } + + // Initialize a class and fill in the value for a static field. + // This is useful when the compile was dependent on the value of + // static fields but it's impossible to properly rerun the static + // initiailizer. + void process_staticfield(TRAPS) { + InstanceKlass* k = (InstanceKlass *)parse_klass(CHECK); + + if (ReplaySuppressInitializers == 0 || + ReplaySuppressInitializers == 2 && k->class_loader() == NULL) { + return; + } + + assert(k->is_initialized(), "must be"); + + const char* field_name = parse_escaped_string();; + const char* field_signature = parse_string(); + fieldDescriptor fd; + Symbol* name = SymbolTable::lookup(field_name, (int)strlen(field_name), CHECK); + Symbol* sig = SymbolTable::lookup(field_signature, (int)strlen(field_signature), CHECK); + if (!k->find_local_field(name, sig, &fd) || + !fd.is_static() || + fd.has_initial_value()) { + report_error(field_name); + return; + } + + oop java_mirror = k->java_mirror(); + if (field_signature[0] == '[') { + int length = parse_int("array length"); + oop value = NULL; + + if (field_signature[1] == '[') { + // multi dimensional array + ArrayKlass* kelem = (ArrayKlass *)parse_klass(CHECK); + int rank = 0; + while (field_signature[rank] == '[') { + rank++; + } + int* dims = NEW_RESOURCE_ARRAY(int, rank); + dims[0] = length; + for (int i = 1; i < rank; i++) { + dims[i] = 1; // These aren't relevant to the compiler + } + value = kelem->multi_allocate(rank, dims, CHECK); + } else { + if (strcmp(field_signature, "[B") == 0) { + value = oopFactory::new_byteArray(length, CHECK); + } else if (strcmp(field_signature, "[Z") == 0) { + value = oopFactory::new_boolArray(length, CHECK); + } else if (strcmp(field_signature, "[C") == 0) { + value = oopFactory::new_charArray(length, CHECK); + } else if (strcmp(field_signature, "[S") == 0) { + value = oopFactory::new_shortArray(length, CHECK); + } else if (strcmp(field_signature, "[F") == 0) { + value = oopFactory::new_singleArray(length, CHECK); + } else if (strcmp(field_signature, "[D") == 0) { + value = oopFactory::new_doubleArray(length, CHECK); + } else if (strcmp(field_signature, "[I") == 0) { + value = oopFactory::new_intArray(length, CHECK); + } else if (strcmp(field_signature, "[J") == 0) { + value = oopFactory::new_longArray(length, CHECK); + } else if (field_signature[0] == '[' && field_signature[1] == 'L') { + KlassHandle kelem = resolve_klass(field_signature + 1, CHECK); + value = oopFactory::new_objArray(kelem(), length, CHECK); + } else { + report_error("unhandled array staticfield"); + } + } + java_mirror->obj_field_put(fd.offset(), value); + } else { + const char* string_value = parse_escaped_string(); + if (strcmp(field_signature, "I") == 0) { + int value = atoi(string_value); + java_mirror->int_field_put(fd.offset(), value); + } else if (strcmp(field_signature, "B") == 0) { + int value = atoi(string_value); + java_mirror->byte_field_put(fd.offset(), value); + } else if (strcmp(field_signature, "C") == 0) { + int value = atoi(string_value); + java_mirror->char_field_put(fd.offset(), value); + } else if (strcmp(field_signature, "S") == 0) { + int value = atoi(string_value); + java_mirror->short_field_put(fd.offset(), value); + } else if (strcmp(field_signature, "Z") == 0) { + int value = atol(string_value); + java_mirror->bool_field_put(fd.offset(), value); + } else if (strcmp(field_signature, "J") == 0) { + jlong value; + if (sscanf(string_value, INT64_FORMAT, &value) != 1) { + fprintf(stderr, "Error parsing long: %s\n", string_value); + return; + } + java_mirror->long_field_put(fd.offset(), value); + } else if (strcmp(field_signature, "F") == 0) { + float value = atof(string_value); + java_mirror->float_field_put(fd.offset(), value); + } else if (strcmp(field_signature, "D") == 0) { + double value = atof(string_value); + java_mirror->double_field_put(fd.offset(), value); + } else if (strcmp(field_signature, "Ljava/lang/String;") == 0) { + Handle value = java_lang_String::create_from_str(string_value, CHECK); + java_mirror->obj_field_put(fd.offset(), value()); + } else if (field_signature[0] == 'L') { + Symbol* klass_name = SymbolTable::lookup(field_signature, (int)strlen(field_signature), CHECK); + KlassHandle kelem = resolve_klass(field_signature, CHECK); + oop value = ((InstanceKlass*)kelem())->allocate_instance(CHECK); + java_mirror->obj_field_put(fd.offset(), value); + } else { + report_error("unhandled staticfield"); + } + } + } + +#if INCLUDE_JVMTI + void process_JvmtiExport(TRAPS) { + const char* field = parse_string(); + bool value = parse_int("JvmtiExport flag") != 0; + if (strcmp(field, "can_access_local_variables") == 0) { + JvmtiExport::set_can_access_local_variables(value); + } else if (strcmp(field, "can_hotswap_or_post_breakpoint") == 0) { + JvmtiExport::set_can_hotswap_or_post_breakpoint(value); + } else if (strcmp(field, "can_post_on_exceptions") == 0) { + JvmtiExport::set_can_post_on_exceptions(value); + } else { + report_error("Unrecognized JvmtiExport directive"); + } + } +#endif // INCLUDE_JVMTI + + // Create and initialize a record for a ciMethod + ciMethodRecord* new_ciMethod(Method* method) { + ciMethodRecord* rec = NEW_RESOURCE_OBJ(ciMethodRecord); + rec->klass = method->method_holder()->name()->as_utf8(); + rec->method = method->name()->as_utf8(); + rec->signature = method->signature()->as_utf8(); + ci_method_records.append(rec); + return rec; + } + + // Lookup data for a ciMethod + ciMethodRecord* find_ciMethodRecord(Method* method) { + const char* klass_name = method->method_holder()->name()->as_utf8(); + const char* method_name = method->name()->as_utf8(); + const char* signature = method->signature()->as_utf8(); + for (int i = 0; i < ci_method_records.length(); i++) { + ciMethodRecord* rec = ci_method_records.at(i); + if (strcmp(rec->klass, klass_name) == 0 && + strcmp(rec->method, method_name) == 0 && + strcmp(rec->signature, signature) == 0) { + return rec; + } + } + return NULL; + } + + // Create and initialize a record for a ciMethodData + ciMethodDataRecord* new_ciMethodData(Method* method) { + ciMethodDataRecord* rec = NEW_RESOURCE_OBJ(ciMethodDataRecord); + rec->klass = method->method_holder()->name()->as_utf8(); + rec->method = method->name()->as_utf8(); + rec->signature = method->signature()->as_utf8(); + ci_method_data_records.append(rec); + return rec; + } + + // Lookup data for a ciMethodData + ciMethodDataRecord* find_ciMethodDataRecord(Method* method) { + const char* klass_name = method->method_holder()->name()->as_utf8(); + const char* method_name = method->name()->as_utf8(); + const char* signature = method->signature()->as_utf8(); + for (int i = 0; i < ci_method_data_records.length(); i++) { + ciMethodDataRecord* rec = ci_method_data_records.at(i); + if (strcmp(rec->klass, klass_name) == 0 && + strcmp(rec->method, method_name) == 0 && + strcmp(rec->signature, signature) == 0) { + return rec; + } + } + return NULL; + } + + const char* error_message() { + return _error_message; + } + + void reset() { + _error_message = NULL; + ci_method_records.clear(); + ci_method_data_records.clear(); + } + + // Take an ascii string contain \u#### escapes and convert it to utf8 + // in place. + static void unescape_string(char* value) { + char* from = value; + char* to = value; + while (*from != '\0') { + if (*from != '\\') { + *from++ = *to++; + } else { + switch (from[1]) { + case 'u': { + from += 2; + jchar value=0; + for (int i=0; i<4; i++) { + char c = *from++; + switch (c) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + value = (value << 4) + c - '0'; + break; + case 'a': case 'b': case 'c': + case 'd': case 'e': case 'f': + value = (value << 4) + 10 + c - 'a'; + break; + case 'A': case 'B': case 'C': + case 'D': case 'E': case 'F': + value = (value << 4) + 10 + c - 'A'; + break; + default: + ShouldNotReachHere(); + } + } + UNICODE::convert_to_utf8(&value, 1, to); + to++; + break; + } + case 't': *to++ = '\t'; from += 2; break; + case 'n': *to++ = '\n'; from += 2; break; + case 'r': *to++ = '\r'; from += 2; break; + case 'f': *to++ = '\f'; from += 2; break; + default: + ShouldNotReachHere(); + } + } + } + *from = *to; + } +}; + +void ciReplay::replay(TRAPS) { + int exit_code = replay_impl(THREAD); + + Threads::destroy_vm(); + + vm_exit(exit_code); +} + +int ciReplay::replay_impl(TRAPS) { + HandleMark hm; + ResourceMark rm; + // Make sure we don't run with background compilation + BackgroundCompilation = false; + + if (ReplaySuppressInitializers > 2) { + // ReplaySuppressInitializers > 2 means that we want to allow + // normal VM bootstrap but once we get into the replay itself + // don't allow any intializers to be run. + ReplaySuppressInitializers = 1; + } + + // Load and parse the replay data + CompileReplay rp(ReplayDataFile, THREAD); + int exit_code = 0; + if (rp.can_replay()) { + rp.process(THREAD); + } else { + exit_code = 1; + return exit_code; + } + + if (HAS_PENDING_EXCEPTION) { + oop throwable = PENDING_EXCEPTION; + CLEAR_PENDING_EXCEPTION; + java_lang_Throwable::print(throwable, tty); + tty->cr(); + java_lang_Throwable::print_stack_trace(throwable, tty); + tty->cr(); + exit_code = 2; + } + + if (rp.had_error()) { + tty->print_cr("Failed on %s", rp.error_message()); + exit_code = 1; + } + return exit_code; +} + + +void ciReplay::initialize(ciMethodData* m) { + if (replay_state == NULL) { + return; + } + + ASSERT_IN_VM; + ResourceMark rm; + + Method* method = m->get_MethodData()->method(); + ciMethodDataRecord* rec = replay_state->find_ciMethodDataRecord(method); + if (rec == NULL) { + // This indicates some mismatch with the original environment and + // the replay environment though it's not always enough to + // interfere with reproducing a bug + tty->print_cr("Warning: requesting ciMethodData record for method with no data: "); + method->print_name(tty); + tty->cr(); + } else { + m->_state = rec->state; + m->_current_mileage = rec->current_mileage; + if (rec->data_length != 0) { + assert(m->_data_size == rec->data_length * (int)sizeof(rec->data[0]), "must agree"); + + // Write the correct ciObjects back into the profile data + ciEnv* env = ciEnv::current(); + for (int i = 0; i < rec->oops_length; i++) { + KlassHandle *h = (KlassHandle *)rec->oops_handles[i]; + *(ciMetadata**)(rec->data + rec->oops_offsets[i]) = + env->get_metadata((*h)()); + } + // Copy the updated profile data into place as intptr_ts +#ifdef _LP64 + Copy::conjoint_jlongs_atomic((jlong *)rec->data, (jlong *)m->_data, rec->data_length); +#else + Copy::conjoint_jints_atomic((jint *)rec->data, (jint *)m->_data, rec->data_length); +#endif + } + + // copy in the original header + Copy::conjoint_jbytes(rec->orig_data, (char*)&m->_orig, rec->orig_data_length); + } +} + + +bool ciReplay::should_not_inline(ciMethod* method) { + if (replay_state == NULL) { + return false; + } + + VM_ENTRY_MARK; + // ciMethod without a record shouldn't be inlined. + return replay_state->find_ciMethodRecord(method->get_Method()) == NULL; +} + + +void ciReplay::initialize(ciMethod* m) { + if (replay_state == NULL) { + return; + } + + ASSERT_IN_VM; + ResourceMark rm; + + Method* method = m->get_Method(); + ciMethodRecord* rec = replay_state->find_ciMethodRecord(method); + if (rec == NULL) { + // This indicates some mismatch with the original environment and + // the replay environment though it's not always enough to + // interfere with reproducing a bug + tty->print_cr("Warning: requesting ciMethod record for method with no data: "); + method->print_name(tty); + tty->cr(); + } else { + // m->_instructions_size = rec->instructions_size; + m->_instructions_size = -1; + m->_interpreter_invocation_count = rec->interpreter_invocation_count; + m->_interpreter_throwout_count = rec->interpreter_throwout_count; + method->invocation_counter()->_counter = rec->invocation_counter; + method->backedge_counter()->_counter = rec->backedge_counter; + } +} + +bool ciReplay::is_loaded(Method* method) { + if (replay_state == NULL) { + return true; + } + + ASSERT_IN_VM; + ResourceMark rm; + + ciMethodRecord* rec = replay_state->find_ciMethodRecord(method); + return rec != NULL; +} +#endif diff --git a/src/share/vm/ci/ciReplay.hpp b/src/share/vm/ci/ciReplay.hpp new file mode 100644 index 000000000..5966d3ece --- /dev/null +++ b/src/share/vm/ci/ciReplay.hpp @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + * + */ + +#ifndef SHARE_VM_CI_CIREPLAY_HPP +#define SHARE_VM_CI_CIREPLAY_HPP + +#include "ci/ciMethod.hpp" + +// ciReplay + +class ciReplay { + CI_PACKAGE_ACCESS + +#ifdef ASSERT + private: + static int replay_impl(TRAPS); + + public: + static void replay(TRAPS); + + // These are used by the CI to fill in the cached data from the + // replay file when replaying compiles. + static void initialize(ciMethodData* method); + static void initialize(ciMethod* method); + + static bool is_loaded(Method* method); + static bool is_loaded(Klass* klass); + + static bool should_not_inline(ciMethod* method); + +#endif +}; + +#endif // SHARE_VM_CI_CIREPLAY_HPP diff --git a/src/share/vm/ci/ciSymbol.cpp b/src/share/vm/ci/ciSymbol.cpp index 26b3e4d56..1a89adf5d 100644 --- a/src/share/vm/ci/ciSymbol.cpp +++ b/src/share/vm/ci/ciSymbol.cpp @@ -63,6 +63,11 @@ const char* ciSymbol::as_utf8() { return s->as_utf8(); } +// The text of the symbol as a null-terminated C string. +const char* ciSymbol::as_quoted_ascii() { + GUARDED_VM_QUICK_ENTRY(return get_symbol()->as_quoted_ascii();) +} + // ------------------------------------------------------------------ // ciSymbol::base const jbyte* ciSymbol::base() { diff --git a/src/share/vm/ci/ciSymbol.hpp b/src/share/vm/ci/ciSymbol.hpp index 642be4625..d54b54009 100644 --- a/src/share/vm/ci/ciSymbol.hpp +++ b/src/share/vm/ci/ciSymbol.hpp @@ -73,6 +73,9 @@ public: const char* as_utf8(); int utf8_length(); + // The text of the symbol as ascii with all non-printable characters quoted as \u#### + const char* as_quoted_ascii(); + // Return the i-th utf8 byte, where i < utf8_length int byte_at(int i); diff --git a/src/share/vm/ci/ciUtilities.hpp b/src/share/vm/ci/ciUtilities.hpp index 9788f77a7..1a075bf6e 100644 --- a/src/share/vm/ci/ciUtilities.hpp +++ b/src/share/vm/ci/ciUtilities.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -80,6 +80,9 @@ #define GUARDED_VM_ENTRY(action) \ {if (IS_IN_VM) { action } else { VM_ENTRY_MARK; { action }}} +#define GUARDED_VM_QUICK_ENTRY(action) \ + {if (IS_IN_VM) { action } else { VM_QUICK_ENTRY_MARK; { action }}} + // Redefine this later. #define KILL_COMPILE_ON_FATAL_(result) \ THREAD); \ diff --git a/src/share/vm/classfile/javaClasses.cpp b/src/share/vm/classfile/javaClasses.cpp index c414079e9..5662fabad 100644 --- a/src/share/vm/classfile/javaClasses.cpp +++ b/src/share/vm/classfile/javaClasses.cpp @@ -348,6 +348,22 @@ unsigned int java_lang_String::to_hash(oop java_string) { return java_lang_String::to_hash(value->char_at_addr(offset), length); } +char* java_lang_String::as_quoted_ascii(oop java_string) { + typeArrayOop value = java_lang_String::value(java_string); + int offset = java_lang_String::offset(java_string); + int length = java_lang_String::length(java_string); + + jchar* base = (length == 0) ? NULL : value->char_at_addr(offset); + if (base == NULL) return NULL; + + int result_length = UNICODE::quoted_ascii_length(base, length) + 1; + char* result = NEW_RESOURCE_ARRAY(char, result_length); + UNICODE::as_quoted_ascii(base, length, result, result_length); + assert(result_length >= length + 1, "must not be shorter"); + assert(result_length == (int)strlen(result) + 1, "must match"); + return result; +} + unsigned int java_lang_String::hash_string(oop java_string) { int length = java_lang_String::length(java_string); // Zero length string doesn't hash necessarily hash to zero. diff --git a/src/share/vm/classfile/javaClasses.hpp b/src/share/vm/classfile/javaClasses.hpp index 902099f19..04e0214d2 100644 --- a/src/share/vm/classfile/javaClasses.hpp +++ b/src/share/vm/classfile/javaClasses.hpp @@ -154,6 +154,8 @@ class java_lang_String : AllStatic { static char* as_utf8_string(oop java_string, int start, int len); static char* as_platform_dependent_str(Handle java_string, TRAPS); static jchar* as_unicode_string(oop java_string, int& length); + // produce an ascii string with all other values quoted using \u#### + static char* as_quoted_ascii(oop java_string); // Compute the hash value for a java.lang.String object which would // contain the characters passed in. diff --git a/src/share/vm/code/dependencies.cpp b/src/share/vm/code/dependencies.cpp index 93db50afe..d81fb3f3f 100644 --- a/src/share/vm/code/dependencies.cpp +++ b/src/share/vm/code/dependencies.cpp @@ -569,6 +569,7 @@ void Dependencies::print_dependency(DepType dept, int nargs, DepArgument args[], void Dependencies::DepStream::log_dependency(Klass* witness) { if (_deps == NULL && xtty == NULL) return; // fast cutout for runtime + ResourceMark rm; int nargs = argument_count(); DepArgument args[max_arg_count]; for (int j = 0; j < nargs; j++) { diff --git a/src/share/vm/interpreter/invocationCounter.hpp b/src/share/vm/interpreter/invocationCounter.hpp index 38725907e..db896d8ad 100644 --- a/src/share/vm/interpreter/invocationCounter.hpp +++ b/src/share/vm/interpreter/invocationCounter.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,6 +40,7 @@ class InvocationCounter VALUE_OBJ_CLASS_SPEC { friend class VMStructs; + friend class ciReplay; private: // bit no: |31 3| 2 | 1 0 | unsigned int _counter; // format: [count|carry|state] @@ -85,6 +86,8 @@ class InvocationCounter VALUE_OBJ_CLASS_SPEC { void set_carry(); // set the sticky carry bit void set_carry_flag() { _counter |= carry_mask; } + int raw_counter() { return _counter; } + // Accessors State state() const { return (State)(_counter & state_mask); } bool carry() const { return (_counter & carry_mask) != 0; } diff --git a/src/share/vm/oops/instanceKlass.cpp b/src/share/vm/oops/instanceKlass.cpp index c5b20d8ff..2f1ef7006 100644 --- a/src/share/vm/oops/instanceKlass.cpp +++ b/src/share/vm/oops/instanceKlass.cpp @@ -1052,6 +1052,13 @@ Method* InstanceKlass::class_initializer() { } void InstanceKlass::call_class_initializer_impl(instanceKlassHandle this_oop, TRAPS) { + if (ReplayCompiles && + (ReplaySuppressInitializers == 1 || + ReplaySuppressInitializers >= 2 && this_oop->class_loader() != NULL)) { + // Hide the existence of the initializer for the purpose of replaying the compile + return; + } + methodHandle h_method(THREAD, this_oop->class_initializer()); assert(!this_oop->is_initialized(), "we cannot initialize twice"); if (TraceClassInitialization) { diff --git a/src/share/vm/oops/instanceKlass.hpp b/src/share/vm/oops/instanceKlass.hpp index f427f338c..ad59ab063 100644 --- a/src/share/vm/oops/instanceKlass.hpp +++ b/src/share/vm/oops/instanceKlass.hpp @@ -133,6 +133,7 @@ class OopMapBlock VALUE_OBJ_CLASS_SPEC { class InstanceKlass: public Klass { friend class VMStructs; friend class ClassFileParser; + friend class CompileReplay; protected: // Constructor diff --git a/src/share/vm/oops/symbol.cpp b/src/share/vm/oops/symbol.cpp index 6a8191cbe..f2253dbcc 100644 --- a/src/share/vm/oops/symbol.cpp +++ b/src/share/vm/oops/symbol.cpp @@ -153,17 +153,15 @@ char* Symbol::as_C_string_flexible_buffer(Thread* t, void Symbol::print_symbol_on(outputStream* st) const { st = st ? st : tty; - int length = UTF8::unicode_length((const char*)bytes(), utf8_length()); - const char *ptr = (const char *)bytes(); - jchar value; - for (int index = 0; index < length; index++) { - ptr = UTF8::next(ptr, &value); - if (value >= 32 && value < 127 || value == '\'' || value == '\\') { - st->put(value); - } else { - st->print("\\u%04x", value); - } - } + st->print("%s", as_quoted_ascii()); +} + +char* Symbol::as_quoted_ascii() const { + const char *ptr = (const char *)&_body[0]; + int quoted_length = UTF8::quoted_ascii_length(ptr, utf8_length()); + char* result = NEW_RESOURCE_ARRAY(char, quoted_length + 1); + UTF8::as_quoted_ascii(ptr, result, quoted_length + 1); + return result; } jchar* Symbol::as_unicode(int& length) const { diff --git a/src/share/vm/oops/symbol.hpp b/src/share/vm/oops/symbol.hpp index 90ec5c176..55867d2b0 100644 --- a/src/share/vm/oops/symbol.hpp +++ b/src/share/vm/oops/symbol.hpp @@ -189,6 +189,8 @@ class Symbol : public MetaspaceObj { // Use buf if needed buffer length is <= size. char* as_C_string_flexible_buffer(Thread* t, char* buf, int size) const; + // Returns an escaped form of a Java string. + char* as_quoted_ascii() const; // Returns a null terminated utf8 string in a resource array char* as_utf8() const { return as_C_string(); } diff --git a/src/share/vm/opto/bytecodeInfo.cpp b/src/share/vm/opto/bytecodeInfo.cpp index 1c08a6ebf..80117960d 100644 --- a/src/share/vm/opto/bytecodeInfo.cpp +++ b/src/share/vm/opto/bytecodeInfo.cpp @@ -23,6 +23,7 @@ */ #include "precompiled.hpp" +#include "ci/ciReplay.hpp" #include "classfile/systemDictionary.hpp" #include "classfile/vmSymbols.hpp" #include "compiler/compileBroker.hpp" @@ -150,7 +151,7 @@ const char* InlineTree::should_inline(ciMethod* callee_method, ciMethod* caller_ } else { // Not hot. Check for medium-sized pre-existing nmethod at cold sites. if (callee_method->has_compiled_code() && - callee_method->instructions_size(CompLevel_full_optimization) > inline_small_code_size) + callee_method->instructions_size() > inline_small_code_size) return "already compiled into a medium method"; } if (size > max_inline_size) { @@ -192,7 +193,7 @@ const char* InlineTree::should_not_inline(ciMethod *callee_method, ciMethod* cal } if (callee_method->has_compiled_code() && - callee_method->instructions_size(CompLevel_full_optimization) > InlineSmallCode) { + callee_method->instructions_size() > InlineSmallCode) { wci_result->set_profit(wci_result->profit() * 0.1); // %%% adjust wci_result->size()? } @@ -216,7 +217,7 @@ const char* InlineTree::should_not_inline(ciMethod *callee_method, ciMethod* cal // Now perform checks which are heuristic if (callee_method->has_compiled_code() && - callee_method->instructions_size(CompLevel_full_optimization) > InlineSmallCode) { + callee_method->instructions_size() > InlineSmallCode) { return "already compiled into a big method"; } @@ -235,6 +236,12 @@ const char* InlineTree::should_not_inline(ciMethod *callee_method, ciMethod* cal return "disallowed by CompilerOracle"; } +#ifndef PRODUCT + if (ciReplay::should_not_inline(callee_method)) { + return "disallowed by ciReplay"; + } +#endif + if (UseStringCache) { // Do not inline StringCache::profile() method used only at the beginning. if (callee_method->name() == ciSymbol::profile_name() && diff --git a/src/share/vm/prims/jni.cpp b/src/share/vm/prims/jni.cpp index aeb62798b..1678053c6 100644 --- a/src/share/vm/prims/jni.cpp +++ b/src/share/vm/prims/jni.cpp @@ -24,6 +24,7 @@ */ #include "precompiled.hpp" +#include "ci/ciReplay.hpp" #include "classfile/altHashing.hpp" #include "classfile/classLoader.hpp" #include "classfile/javaClasses.hpp" @@ -5151,6 +5152,7 @@ _JNI_IMPORT_OR_EXPORT_ jint JNICALL JNI_CreateJavaVM(JavaVM **vm, void **penv, v // Check if we should compile all classes on bootclasspath NOT_PRODUCT(if (CompileTheWorld) ClassLoader::compile_the_world();) + NOT_PRODUCT(if (ReplayCompiles) ciReplay::replay(thread);) // Since this is not a JVM_ENTRY we have to set the thread state manually before leaving. ThreadStateTransition::transition_and_fence(thread, _thread_in_vm, _thread_in_native); } else { diff --git a/src/share/vm/prims/jvmtiExport.hpp b/src/share/vm/prims/jvmtiExport.hpp index 7dcab6831..1100d5269 100644 --- a/src/share/vm/prims/jvmtiExport.hpp +++ b/src/share/vm/prims/jvmtiExport.hpp @@ -64,6 +64,8 @@ class AttachOperation; // class JvmtiExport : public AllStatic { friend class VMStructs; + friend class CompileReplay; + private: #if INCLUDE_JVMTI diff --git a/src/share/vm/runtime/compilationPolicy.cpp b/src/share/vm/runtime/compilationPolicy.cpp index 73b8f8393..713163a10 100644 --- a/src/share/vm/runtime/compilationPolicy.cpp +++ b/src/share/vm/runtime/compilationPolicy.cpp @@ -97,6 +97,9 @@ void CompilationPolicy::completed_vm_startup() { // This is intended to force compiles for methods (usually for // debugging) that would otherwise be interpreted for some reason. bool CompilationPolicy::must_be_compiled(methodHandle m, int comp_level) { + // Don't allow Xcomp to cause compiles in replay mode + if (ReplayCompiles) return false; + if (m->has_compiled_code()) return false; // already compiled if (!can_be_compiled(m, comp_level)) return false; @@ -322,6 +325,16 @@ nmethod* NonTieredCompPolicy::event(methodHandle method, methodHandle inlinee, i return NULL; } } + if (CompileTheWorld || ReplayCompiles) { + // Don't trigger other compiles in testing mode + if (bci == InvocationEntryBci) { + reset_counter_for_invocation_event(method); + } else { + reset_counter_for_back_branch_event(method); + } + return NULL; + } + if (bci == InvocationEntryBci) { // when code cache is full, compilation gets switched off, UseCompiler // is set to false diff --git a/src/share/vm/runtime/globals.hpp b/src/share/vm/runtime/globals.hpp index 62ad97f89..785965ca3 100644 --- a/src/share/vm/runtime/globals.hpp +++ b/src/share/vm/runtime/globals.hpp @@ -3189,6 +3189,26 @@ class CommandLineFlags { product(ccstrlist, CompileCommand, "", \ "Prepend to .hotspot_compiler; e.g. log,java/lang/String.") \ \ + develop(bool, ReplayCompiles, false, \ + "Enable replay of compilations from ReplayDataFile") \ + \ + develop(ccstr, ReplayDataFile, "replay.txt", \ + "file containing compilation replay information") \ + \ + develop(intx, ReplaySuppressInitializers, 2, \ + "Controls handling of class initialization during replay" \ + "0 - don't do anything special" \ + "1 - treat all class initializers as empty" \ + "2 - treat class initializers for application classes as empty" \ + "3 - allow all class initializers to run during bootstrap but" \ + " pretend they are empty after starting replay") \ + \ + develop(bool, ReplayIgnoreInitErrors, false, \ + "Ignore exceptions thrown during initialization for replay") \ + \ + develop(bool, DumpReplayDataOnError, true, \ + "record replay data for crashing compiler threads") \ + \ product(bool, CICompilerCountPerCPU, false, \ "1 compiler thread for log(N CPUs)") \ \ diff --git a/src/share/vm/runtime/vmStructs.cpp b/src/share/vm/runtime/vmStructs.cpp index eb6d3cd78..484566125 100644 --- a/src/share/vm/runtime/vmStructs.cpp +++ b/src/share/vm/runtime/vmStructs.cpp @@ -993,6 +993,7 @@ typedef BinaryTreeDictionary MetablockTreeDictionary; \ nonstatic_field(ciMethod, _interpreter_invocation_count, int) \ nonstatic_field(ciMethod, _interpreter_throwout_count, int) \ + nonstatic_field(ciMethod, _instructions_size, int) \ \ nonstatic_field(ciMethodData, _data_size, int) \ nonstatic_field(ciMethodData, _state, u_char) \ diff --git a/src/share/vm/utilities/array.hpp b/src/share/vm/utilities/array.hpp index 9690529c5..5578ed9b6 100644 --- a/src/share/vm/utilities/array.hpp +++ b/src/share/vm/utilities/array.hpp @@ -353,9 +353,9 @@ protected: // sort the array. bool contains(const T& x) const { return index_of(x) >= 0; } - T at(int i) const { return _data[i]; } - void at_put(const int i, const T& x) { _data[i] = x; } - T* adr_at(const int i) { return &_data[i]; } + T at(int i) const { assert(i >= 0 && i< _length, err_msg_res("oob: 0 <= %d < %d", i, _length)); return _data[i]; } + void at_put(const int i, const T& x) { assert(i >= 0 && i< _length, err_msg_res("oob: 0 <= %d < %d", i, _length)); _data[i] = x; } + T* adr_at(const int i) { assert(i >= 0 && i< _length, err_msg_res("oob: 0 <= %d < %d", i, _length)); return &_data[i]; } int find(const T& x) { return index_of(x); } T at_acquire(const int which) { return OrderAccess::load_acquire(adr_at(which)); } diff --git a/src/share/vm/utilities/utf8.cpp b/src/share/vm/utilities/utf8.cpp index be7d18815..da470b18c 100644 --- a/src/share/vm/utilities/utf8.cpp +++ b/src/share/vm/utilities/utf8.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -147,7 +147,7 @@ static u_char* utf8_write(u_char* base, jchar ch) { void UTF8::convert_to_unicode(const char* utf8_str, jchar* unicode_str, int unicode_length) { unsigned char ch; - const char *ptr = (const char *)utf8_str; + const char *ptr = utf8_str; int index = 0; /* ASCII case loop optimization */ @@ -162,6 +162,119 @@ void UTF8::convert_to_unicode(const char* utf8_str, jchar* unicode_str, int unic } } +// returns the quoted ascii length of a 0-terminated utf8 string +int UTF8::quoted_ascii_length(const char* utf8_str, int utf8_length) { + const char *ptr = utf8_str; + const char* end = ptr + utf8_length; + int result = 0; + while (ptr < end) { + jchar c; + ptr = UTF8::next(ptr, &c); + if (c >= 32 && c < 127) { + result++; + } else { + result += 6; + } + } + return result; +} + +// converts a utf8 string to quoted ascii +void UTF8::as_quoted_ascii(const char* utf8_str, char* buf, int buflen) { + const char *ptr = utf8_str; + char* p = buf; + char* end = buf + buflen; + while (*ptr != '\0') { + jchar c; + ptr = UTF8::next(ptr, &c); + if (c >= 32 && c < 127) { + if (p + 1 >= end) break; // string is truncated + *p++ = (char)c; + } else { + if (p + 6 >= end) break; // string is truncated + sprintf(p, "\\u%04x", c); + p += 6; + } + } + *p = '\0'; +} + + +const char* UTF8::from_quoted_ascii(const char* quoted_ascii_str) { + const char *ptr = quoted_ascii_str; + char* result = NULL; + while (*ptr != '\0') { + char c = *ptr; + if (c < 32 || c >= 127) break; + } + if (*ptr == '\0') { + // nothing to do so return original string + return quoted_ascii_str; + } + // everything up to this point was ok. + int length = ptr - quoted_ascii_str; + char* buffer = NULL; + for (int round = 0; round < 2; round++) { + while (*ptr != '\0') { + if (*ptr != '\\') { + if (buffer != NULL) { + buffer[length] = *ptr; + } + length++; + } else { + switch (ptr[1]) { + case 'u': { + ptr += 2; + jchar value=0; + for (int i=0; i<4; i++) { + char c = *ptr++; + switch (c) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + value = (value << 4) + c - '0'; + break; + case 'a': case 'b': case 'c': + case 'd': case 'e': case 'f': + value = (value << 4) + 10 + c - 'a'; + break; + case 'A': case 'B': case 'C': + case 'D': case 'E': case 'F': + value = (value << 4) + 10 + c - 'A'; + break; + default: + ShouldNotReachHere(); + } + } + if (buffer == NULL) { + char utf8_buffer[4]; + char* next = (char*)utf8_write((u_char*)utf8_buffer, value); + length += next - utf8_buffer; + } else { + char* next = (char*)utf8_write((u_char*)&buffer[length], value); + length += next - &buffer[length]; + } + break; + } + case 't': if (buffer != NULL) buffer[length] = '\t'; ptr += 2; length++; break; + case 'n': if (buffer != NULL) buffer[length] = '\n'; ptr += 2; length++; break; + case 'r': if (buffer != NULL) buffer[length] = '\r'; ptr += 2; length++; break; + case 'f': if (buffer != NULL) buffer[length] = '\f'; ptr += 2; length++; break; + default: + ShouldNotReachHere(); + } + } + } + if (round == 0) { + buffer = NEW_RESOURCE_ARRAY(char, length + 1); + ptr = quoted_ascii_str; + } else { + buffer[length] = '\0'; + } + } + return buffer; +} + + // Returns NULL if 'c' it not found. This only works as long // as 'c' is an ASCII character const jbyte* UTF8::strrchr(const jbyte* base, int length, jbyte c) { @@ -242,3 +355,35 @@ void UNICODE::convert_to_utf8(const jchar* base, int length, char* utf8_buffer) } *utf8_buffer = '\0'; } + +// returns the quoted ascii length of a unicode string +int UNICODE::quoted_ascii_length(jchar* base, int length) { + int result = 0; + for (int i = 0; i < length; i++) { + jchar c = base[i]; + if (c >= 32 && c < 127) { + result++; + } else { + result += 6; + } + } + return result; +} + +// converts a utf8 string to quoted ascii +void UNICODE::as_quoted_ascii(const jchar* base, int length, char* buf, int buflen) { + char* p = buf; + char* end = buf + buflen; + for (int index = 0; index < length; index++) { + jchar c = base[index]; + if (c >= 32 && c < 127) { + if (p + 1 >= end) break; // string is truncated + *p++ = (char)c; + } else { + if (p + 6 >= end) break; // string is truncated + sprintf(p, "\\u%04x", c); + p += 6; + } + } + *p = '\0'; +} diff --git a/src/share/vm/utilities/utf8.hpp b/src/share/vm/utilities/utf8.hpp index c56d550ec..69710fcce 100644 --- a/src/share/vm/utilities/utf8.hpp +++ b/src/share/vm/utilities/utf8.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,22 +32,32 @@ class UTF8 : AllStatic { public: - // returns the unicode length of a 0-terminated uft8 string - static int unicode_length(const char* uft8_str); + // returns the unicode length of a 0-terminated utf8 string + static int unicode_length(const char* utf8_str); - // returns the unicode length of a non-0-terminated uft8 string - static int unicode_length(const char* uft8_str, int len); + // returns the unicode length of a non-0-terminated utf8 string + static int unicode_length(const char* utf8_str, int len); - // converts a uft8 string to a unicode string + // converts a utf8 string to a unicode string static void convert_to_unicode(const char* utf8_str, jchar* unicode_buffer, int unicode_length); + // returns the quoted ascii length of a utf8 string + static int quoted_ascii_length(const char* utf8_str, int utf8_length); + + // converts a utf8 string to quoted ascii + static void as_quoted_ascii(const char* utf8_str, char* buf, int buflen); + + // converts a quoted ascii string to utf8 string. returns the original + // string unchanged if nothing needs to be done. + static const char* from_quoted_ascii(const char* quoted_ascii_string); + // decodes the current utf8 character, stores the result in value, - // and returns the end of the current uft8 chararacter. + // and returns the end of the current utf8 chararacter. static char* next(const char* str, jchar* value); // decodes the current utf8 character, gets the supplementary character instead of // the surrogate pair when seeing a supplementary character in string, - // stores the result in value, and returns the end of the current uft8 chararacter. + // stores the result in value, and returns the end of the current utf8 chararacter. static char* next_character(const char* str, jint* value); // Utility methods @@ -79,6 +89,12 @@ class UNICODE : AllStatic { // in resource area unless a buffer is provided. static char* as_utf8(jchar* base, int length); static char* as_utf8(jchar* base, int length, char* buf, int buflen); + + // returns the quoted ascii length of a unicode string + static int quoted_ascii_length(jchar* base, int length); + + // converts a utf8 string to quoted ascii + static void as_quoted_ascii(const jchar* base, int length, char* buf, int buflen); }; #endif // SHARE_VM_UTILITIES_UTF8_HPP diff --git a/src/share/vm/utilities/vmError.cpp b/src/share/vm/utilities/vmError.cpp index 175fe382f..ddb6e1cf8 100644 --- a/src/share/vm/utilities/vmError.cpp +++ b/src/share/vm/utilities/vmError.cpp @@ -1009,6 +1009,15 @@ void VMError::report_and_die() { OnError = NULL; } + static bool skip_replay = false; + if (DumpReplayDataOnError && _thread && _thread->is_Compiler_thread() && !skip_replay) { + skip_replay = true; + ciEnv* env = ciEnv::current(); + if (env != NULL) { + env->dump_replay_data(); + } + } + static bool skip_bug_url = !should_report_bug(first_error->_id); if (!skip_bug_url) { skip_bug_url = true; -- GitLab