# HG changeset patch # User Gilles Duboscq # Date 1336051156 -7200 # Node ID 4471a30a972885be66bc3ac4091f59921edc0096 # Parent 5698355398e38939b00185de27f8e2e23e46352d# Parent 4c3d953f8131774231d80f8cd8195acbf7983ff8 Merge diff -r 5698355398e3 -r 4471a30a9728 graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalOptions.java --- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalOptions.java Thu May 03 15:19:11 2012 +0200 +++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalOptions.java Thu May 03 15:19:16 2012 +0200 @@ -22,6 +22,8 @@ */ package com.oracle.graal.compiler; +import com.oracle.max.criutils.TTY.Filter; + /** * This class encapsulates options that control the behavior of the Graal compiler. * The help message for each option is specified by a {@linkplain #helpMap help map}. @@ -245,6 +247,16 @@ public static int InstanceOfMaxHints = 1; + /** + * The profiling info cache directory. + */ + public static String PICache = null; + + /** + * Filters the methods for which profiling info is loaded from/saved to the {@link #PICache}. + */ + public static String PIFilter = null; + static { // turn detailed assertions on when the general assertions are on (misusing the assert keyword for this) assert (DetailedAsserts = true) == true; diff -r 5698355398e3 -r 4471a30a9728 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/ri/HotSpotMethodResolvedImpl.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/ri/HotSpotMethodResolvedImpl.java Thu May 03 15:19:11 2012 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/ri/HotSpotMethodResolvedImpl.java Thu May 03 15:19:16 2012 +0200 @@ -22,6 +22,7 @@ */ package com.oracle.graal.hotspot.ri; +import java.io.*; import java.lang.annotation.*; import java.lang.reflect.*; import java.util.*; @@ -226,18 +227,72 @@ return compilationComplexity; } + private static final MethodFilter profilingInfoFilter = GraalOptions.PIFilter == null ? null : new MethodFilter(GraalOptions.PIFilter); + + /** + * Determines if the profiling info cache should be used for this method. + */ + private boolean useProfilingInfoCache() { + return GraalOptions.PICache != null && (profilingInfoFilter == null || profilingInfoFilter.matches(this)); + } + + private RiProfilingInfo loadProfilingInfo() { + if (!useProfilingInfoCache()) { + return null; + } + synchronized (this) { + File file = new File(GraalOptions.PICache, JniMangle.mangleMethod(holder, name, signature(), false)); + if (file.exists()) { + try { + SnapshotProfilingInfo snapshot = SnapshotProfilingInfo.load(file, compiler.getRuntime()); + if (snapshot.codeSize() != codeSize) { + // The class file was probably changed - ignore the saved profile + return null; + } + return snapshot; + } catch (Exception e) { + // ignore + } + } + return null; + } + } + + private void saveProfilingInfo(RiProfilingInfo info) { + if (useProfilingInfoCache()) { + synchronized (this) { + String base = JniMangle.mangleMethod(holder, name, signature(), false); + File file = new File(GraalOptions.PICache, base); + File txtFile = new File(GraalOptions.PICache, base + ".txt"); + SnapshotProfilingInfo snapshot = info instanceof SnapshotProfilingInfo ? (SnapshotProfilingInfo) info : new SnapshotProfilingInfo(info); + try { + snapshot.save(file, txtFile); + } catch (IOException e) { + // ignore + } + } + } + } + @Override public RiProfilingInfo profilingInfo() { + RiProfilingInfo info = loadProfilingInfo(); + if (info != null) { + return info; + } + if (GraalOptions.UseProfilingInformation && methodData == null) { methodData = compiler.getCompilerToVM().RiMethod_methodData(this); } - if (methodData == null) { + if (methodData == null || (!methodData.hasNormalData() && !methodData.hasExtraData())) { // Be optimistic and return false for exceptionSeen. A methodDataOop is allocated in case of a deoptimization. - return BaseProfilingInfo.get(RiExceptionSeen.FALSE); + info = BaseProfilingInfo.get(RiExceptionSeen.FALSE); } else { - return new HotSpotProfilingInfo(compiler, methodData); + info = new HotSpotProfilingInfo(compiler, methodData, codeSize); + saveProfilingInfo(info); } + return info; } @Override diff -r 5698355398e3 -r 4471a30a9728 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/ri/HotSpotProfilingInfo.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/ri/HotSpotProfilingInfo.java Thu May 03 15:19:11 2012 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/ri/HotSpotProfilingInfo.java Thu May 03 15:19:16 2012 +0200 @@ -22,6 +22,7 @@ */ package com.oracle.graal.hotspot.ri; +import com.oracle.max.cri.ci.*; import com.oracle.max.cri.ri.*; import com.oracle.graal.debug.*; import com.oracle.graal.hotspot.*; @@ -38,15 +39,22 @@ private int hintBCI; private HotSpotMethodDataAccessor dataAccessor; private HotSpotMethodData methodData; + private final int codeSize; - public HotSpotProfilingInfo(Compiler compiler, HotSpotMethodData methodData) { + public HotSpotProfilingInfo(Compiler compiler, HotSpotMethodData methodData, int codeSize) { super(compiler); this.methodData = methodData; + this.codeSize = codeSize; hintPosition = 0; hintBCI = -1; } @Override + public int codeSize() { + return codeSize; + } + + @Override public RiTypeProfile getTypeProfile(int bci) { findBCI(bci, false); return dataAccessor.getTypeProfile(methodData, position); @@ -140,4 +148,9 @@ this.dataAccessor = dataAccessor; this.position = position; } + + @Override + public String toString() { + return "HotSpotProfilingInfo<" + CiUtil.profileToString(this, null, "; ") + ">"; + } } diff -r 5698355398e3 -r 4471a30a9728 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/ri/HotSpotXirGenerator.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/ri/HotSpotXirGenerator.java Thu May 03 15:19:11 2012 +0200 +++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/ri/HotSpotXirGenerator.java Thu May 03 15:19:16 2012 +0200 @@ -527,7 +527,15 @@ if (hintCount == 0) { assert !exact; if (counters != null) { - incCounter(asm, counter, counters, is(NULL_TYPE, flags) ? CheckcastCounter.noHints_unknown : is(INTERFACE_TYPE, flags) ? CheckcastCounter.noHints_iface : CheckcastCounter.noHints_class); + CheckcastCounter cc; + if (is(NULL_TYPE, flags)) { + cc = CheckcastCounter.noHints_unknown; + } else if (is(INTERFACE_TYPE, flags)) { + cc = CheckcastCounter.noHints_iface; + } else { + cc = CheckcastCounter.noHints_class; + } + incCounter(asm, counter, counters, cc); } checkSubtype(asm, objHub, objHub, hub); diff -r 5698355398e3 -r 4471a30a9728 graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java --- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java Thu May 03 15:19:11 2012 +0200 +++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java Thu May 03 15:19:16 2012 +0200 @@ -148,7 +148,7 @@ if (GraalOptions.PrintProfilingInformation) { TTY.println("Profiling info for " + method); - TTY.println(CiUtil.indent(CiUtil.profileAsString(method), " ")); + TTY.println(CiUtil.indent(CiUtil.profileToString(profilingInfo, method, CiUtil.NEW_LINE), " ")); } // compute the block map, setup exception handlers and get the entrypoint(s) diff -r 5698355398e3 -r 4471a30a9728 graal/com.oracle.max.cri/src/com/oracle/max/cri/ci/CiUtil.java --- a/graal/com.oracle.max.cri/src/com/oracle/max/cri/ci/CiUtil.java Thu May 03 15:19:11 2012 +0200 +++ b/graal/com.oracle.max.cri/src/com/oracle/max/cri/ci/CiUtil.java Thu May 03 15:19:16 2012 +0200 @@ -739,62 +739,70 @@ } /** - * Formats the profiling information associated with a given method to a string. + * Formats some profiling information associated as a string. + * + * @param info the profiling info to format + * @param method an optional method that augments the profile string returned + * @param sep the separator to use for each separate profile record */ - public static String profileAsString(RiResolvedMethod method) { + public static String profileToString(RiProfilingInfo info, RiResolvedMethod method, String sep) { StringBuilder buf = new StringBuilder(100); - buf.append(String.format("canBeStaticallyBound: %b%n", method.canBeStaticallyBound())). - append(String.format("invocationCount: %d%n", method.invocationCount())); - RiProfilingInfo profilingInfo = method.profilingInfo(); - if (profilingInfo != null) { - for (int i = 0; i < method.codeSize(); i++) { - if (profilingInfo.getExecutionCount(i) != -1) { - buf.append(String.format("executionCount@%d: %d%n", i, profilingInfo.getExecutionCount(i))); - } - - if (profilingInfo.getBranchTakenProbability(i) != -1) { - buf.append(String.format("branchProbability@%d: %.3f%n", i, profilingInfo.getBranchTakenProbability(i))); - } + if (method != null) { + buf.append(String.format("canBeStaticallyBound: %b%s", method.canBeStaticallyBound(), sep)). + append(String.format("invocationCount: %d%s", method.invocationCount(), sep)); + } + for (int i = 0; i < info.codeSize(); i++) { + if (info.getExecutionCount(i) != -1) { + buf.append(String.format("executionCount@%d: %d%s", i, info.getExecutionCount(i), sep)); + } - double[] switchProbabilities = profilingInfo.getSwitchProbabilities(i); - if (switchProbabilities != null) { - buf.append(String.format("switchProbabilities@%d:", i)); - for (int j = 0; j < switchProbabilities.length; j++) { - buf.append(String.format(" %.3f", switchProbabilities[j])); - } - buf.append(NEW_LINE); - } - - if (profilingInfo.getExceptionSeen(i) != RiExceptionSeen.FALSE) { - buf.append(String.format("exceptionSeen@%d: %s%n", i, profilingInfo.getExceptionSeen(i).name())); - } - - RiTypeProfile typeProfile = profilingInfo.getTypeProfile(i); - if (typeProfile != null) { - ProfiledType[] ptypes = typeProfile.getTypes(); - if (ptypes != null) { - buf.append(String.format("types@%d:%n", i)); - for (int j = 0; j < ptypes.length; j++) { - ProfiledType ptype = ptypes[j]; - buf.append(String.format(" %.3f %s%n", ptype.probability, ptype.type)); - } - buf.append(String.format(" %.3f %n", typeProfile.getNotRecordedProbability())); - } - } + if (info.getBranchTakenProbability(i) != -1) { + buf.append(String.format("branchProbability@%d: %.3f%s", i, info.getBranchTakenProbability(i), sep)); } - boolean firstDeoptReason = true; - for (RiDeoptReason reason: RiDeoptReason.values()) { - int count = profilingInfo.getDeoptimizationCount(reason); - if (count > 0) { - if (firstDeoptReason) { - buf.append("deoptimization history").append(NEW_LINE); - firstDeoptReason = false; + double[] switchProbabilities = info.getSwitchProbabilities(i); + if (switchProbabilities != null) { + buf.append(String.format("switchProbabilities@%d:", i)); + for (int j = 0; j < switchProbabilities.length; j++) { + buf.append(String.format(" %.3f", switchProbabilities[j])); + } + buf.append(sep); + } + + if (info.getExceptionSeen(i) != RiExceptionSeen.FALSE) { + buf.append(String.format("exceptionSeen@%d: %s%s", i, info.getExceptionSeen(i).name(), sep)); + } + + RiTypeProfile typeProfile = info.getTypeProfile(i); + if (typeProfile != null) { + ProfiledType[] ptypes = typeProfile.getTypes(); + if (ptypes != null) { + buf.append(String.format("types@%d:", i)); + for (int j = 0; j < ptypes.length; j++) { + ProfiledType ptype = ptypes[j]; + buf.append(String.format(" %.3f (%s)%s", ptype.probability, ptype.type, sep)); } - buf.append(String.format(" %s: %d%n", reason.name(), count)); + buf.append(String.format(" %.3f %s", typeProfile.getNotRecordedProbability(), sep)); } } } - return buf.toString(); + + boolean firstDeoptReason = true; + for (RiDeoptReason reason: RiDeoptReason.values()) { + int count = info.getDeoptimizationCount(reason); + if (count > 0) { + if (firstDeoptReason) { + buf.append("deoptimization history").append(sep); + firstDeoptReason = false; + } + buf.append(String.format(" %s: %d%s", reason.name(), count, sep)); + } + } + if (buf.length() == 0) { + return ""; + } + String s = buf.toString(); + assert s.endsWith(sep); + return s.substring(0, s.length() - sep.length()); } } diff -r 5698355398e3 -r 4471a30a9728 graal/com.oracle.max.cri/src/com/oracle/max/cri/ri/RiProfilingInfo.java --- a/graal/com.oracle.max.cri/src/com/oracle/max/cri/ri/RiProfilingInfo.java Thu May 03 15:19:11 2012 +0200 +++ b/graal/com.oracle.max.cri/src/com/oracle/max/cri/ri/RiProfilingInfo.java Thu May 03 15:19:16 2012 +0200 @@ -30,6 +30,12 @@ * as the profiling information may be changed by other Java threads at any time. */ public interface RiProfilingInfo { + + /** + * Gets the length of the code associated with this profile. + */ + int codeSize(); + /** * Returns an estimate of how often the branch at the given byte code was taken. * @return The estimated probability, with 0.0 meaning never and 1.0 meaning always, or -1 if this information is not available. @@ -52,8 +58,8 @@ /** * Returns information if the given BCI did ever throw an exception. - * @return @link{RiExceptionSeen.TRUE} if the instruction has thrown an exception at least once, - * @link{RiExceptionSeen.FALSE} if it never threw an exception, and @link{RiExceptionSeen.UNKNOWN} + * @return {@link RiExceptionSeen#TRUE} if the instruction has thrown an exception at least once, + * {@link RiExceptionSeen#FALSE} if it never threw an exception, and {@link RiExceptionSeen#NOT_SUPPORTED} * if this information was not recorded. */ RiExceptionSeen getExceptionSeen(int bci); diff -r 5698355398e3 -r 4471a30a9728 graal/com.oracle.max.cri/src/com/oracle/max/cri/ri/RiTypeProfile.java --- a/graal/com.oracle.max.cri/src/com/oracle/max/cri/ri/RiTypeProfile.java Thu May 03 15:19:11 2012 +0200 +++ b/graal/com.oracle.max.cri/src/com/oracle/max/cri/ri/RiTypeProfile.java Thu May 03 15:19:16 2012 +0200 @@ -37,7 +37,8 @@ * A profiled type that has a probability. Profiled types are naturally sorted in * descending order of their probabilities. */ - public static class ProfiledType implements Comparable { + public static class ProfiledType implements Comparable, Serializable { + private static final long serialVersionUID = 7838575753661305744L; public final RiResolvedType type; public final double probability; diff -r 5698355398e3 -r 4471a30a9728 graal/com.oracle.max.criutils/src/com/oracle/max/criutils/BaseProfilingInfo.java --- a/graal/com.oracle.max.criutils/src/com/oracle/max/criutils/BaseProfilingInfo.java Thu May 03 15:19:11 2012 +0200 +++ b/graal/com.oracle.max.criutils/src/com/oracle/max/criutils/BaseProfilingInfo.java Thu May 03 15:19:16 2012 +0200 @@ -22,6 +22,7 @@ */ package com.oracle.max.criutils; +import com.oracle.max.cri.ci.*; import com.oracle.max.cri.ri.*; @@ -43,6 +44,11 @@ } @Override + public int codeSize() { + return 0; + } + + @Override public RiTypeProfile getTypeProfile(int bci) { return null; } @@ -75,4 +81,9 @@ public int getDeoptimizationCount(RiDeoptReason reason) { return 0; } + + @Override + public String toString() { + return "BaseProfilingInfo<" + CiUtil.profileToString(this, null, "; ") + ">"; + } } diff -r 5698355398e3 -r 4471a30a9728 graal/com.oracle.max.criutils/src/com/oracle/max/criutils/JniMangle.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.max.criutils/src/com/oracle/max/criutils/JniMangle.java Thu May 03 15:19:16 2012 +0200 @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2007, 2011, 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. + */ +package com.oracle.max.criutils; + +import com.oracle.max.cri.ci.*; +import com.oracle.max.cri.ri.*; + +/** + * A utility for mangling Java method name and signatures into C function names. + * + * @see "http://java.sun.com/j2se/1.5.0/docs/guide/jni/spec/design.html#wp615" + */ +public final class JniMangle { + + private JniMangle() { + } + + /** + * Mangles a given string such that it can be represented as (part of) a valid C function name. + */ + private static String mangle(String name) { + final StringBuilder mangledName = new StringBuilder(100); + final int length = name.length(); + for (int i = 0; i < length; i++) { + final char ch = name.charAt(i); + if (isAlphaNumeric(ch)) { + mangledName.append(ch); + } else if (ch == '_') { + mangledName.append("_1"); + } else if (ch == '.') { + mangledName.append("_"); + } else if (ch == ';') { + mangledName.append("_2"); + } else if (ch == '[') { + mangledName.append("_3"); + } else { + mangledName.append(String.format("%04x", (int) ch)); + } + } + return mangledName.toString(); + } + + /** + * The delimiter in the string returned by {@link #mangleMethod(TypeDescriptor, String, SignatureDescriptor, boolean)} separating + * the short mangled form from the suffix to be added to obtain the long mangled form. + */ + public static final char LONG_NAME_DELIMITER = ' '; + + /** + * Mangles a Java method to the symbol(s) to be used when binding it to a native function. + * If {@code signature} is {@code null}, then a non-qualified symbol is returned. + * Otherwise, a qualified symbol is returned. A qualified symbol has its non-qualified + * prefix separated from its qualifying suffix by {@link #LONG_NAME_DELIMITER} if + * {@code splitSuffix} is {@code true}. + * + * @param declaringClass a fully qualified class descriptor + * @param name a Java method name (not checked here for validity) + * @param signature if non-null, a method signature to include in the mangled name + * @param splitSuffix determines if {@link #LONG_NAME_DELIMITER} should be used as described above + * @return the symbol for the C function as described above + */ + public static String mangleMethod(RiResolvedType declaringClass, String name, RiSignature signature, boolean splitSuffix) { + final StringBuilder result = new StringBuilder(100); + final String declaringClassName = CiUtil.toJavaName(declaringClass); + result.append("Java_").append(mangle(declaringClassName)).append('_').append(mangle(name)); + if (signature != null) { + if (splitSuffix) { + result.append(LONG_NAME_DELIMITER); + } + result.append("__"); + final String sig = signature.asString(); + final String parametersSignature = sig.substring(1, sig.lastIndexOf(')')).replace('/', '.').replace('$', '.'); + result.append(mangle(parametersSignature)); + } + return result.toString(); + } + + private static boolean isAlphaNumeric(char ch) { + return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9'); + } +} diff -r 5698355398e3 -r 4471a30a9728 graal/com.oracle.max.criutils/src/com/oracle/max/criutils/SnapshotProfilingInfo.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.max.criutils/src/com/oracle/max/criutils/SnapshotProfilingInfo.java Thu May 03 15:19:16 2012 +0200 @@ -0,0 +1,176 @@ +/* + * 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. + */ +package com.oracle.max.criutils; + +import java.io.*; + +import com.oracle.max.cri.ci.*; +import com.oracle.max.cri.ri.*; + +/** + * A profiling info snapshot that can be {@linkplain #save(File, File) saved} to + * and {@linkplain #load(File, RiRuntime) loaded} from a file. + */ +public class SnapshotProfilingInfo implements RiProfilingInfo, Serializable { + + private static final long serialVersionUID = -5837615128782960391L; + private final double[] branchTaken; + private final double[][] switches; + private final RiTypeProfile[] typeProfiles; + private final RiExceptionSeen[] exceptions; + private final int[] executions; + private final int[] deopts; + + public SnapshotProfilingInfo(RiProfilingInfo other) { + int codeSize = other.codeSize(); + branchTaken = new double[codeSize]; + switches = new double[codeSize][]; + typeProfiles = new RiTypeProfile[codeSize]; + exceptions = new RiExceptionSeen[codeSize]; + executions = new int[codeSize]; + deopts = new int[RiDeoptReason.values().length]; + + for (int bci = 0; bci < codeSize; bci++) { + executions[bci] = other.getExecutionCount(bci); + exceptions[bci] = other.getExceptionSeen(bci); + branchTaken[bci] = other.getBranchTakenProbability(bci); + switches[bci] = other.getSwitchProbabilities(bci); + typeProfiles[bci] = other.getTypeProfile(bci); + } + for (RiDeoptReason reason: RiDeoptReason.values()) { + deopts[reason.ordinal()] = other.getDeoptimizationCount(reason); + } + } + + @Override + public int codeSize() { + return branchTaken.length; + } + + public double getBranchTakenProbability(int bci) { + return bci < branchTaken.length ? branchTaken[bci] : -1D; + } + public double[] getSwitchProbabilities(int bci) { + return bci < switches.length ? switches[bci] : null; + } + public RiTypeProfile getTypeProfile(int bci) { + return bci < typeProfiles.length ? typeProfiles[bci] : null; + } + public RiExceptionSeen getExceptionSeen(int bci) { + return bci < exceptions.length ? exceptions[bci] : RiExceptionSeen.NOT_SUPPORTED; + } + public int getExecutionCount(int bci) { + return bci < executions.length ? executions[bci] : -1; + } + public int getDeoptimizationCount(RiDeoptReason reason) { + return deopts[reason.ordinal()]; + } + + @Override + public String toString() { + return CiUtil.profileToString(this, null, "; "); + } + + /** + * Deserializes a profile snapshot from a file. + * + * @param file a file created by {@link #save(File, File)} + * @param runtime the runtime used to resolve {@link RiResolvedType}s during deserialization + * @return + * @throws ClassNotFoundException + * @throws IOException + */ + public static SnapshotProfilingInfo load(File file, RiRuntime runtime) throws ClassNotFoundException, IOException { + SnapshotProfilingInfo.SnapshotObjectInputStream ois = new SnapshotObjectInputStream(new BufferedInputStream(new FileInputStream(file), (int) file.length()), runtime); + try { + return (SnapshotProfilingInfo) ois.readObject(); + } finally { + ois.close(); + } + } + + /** + * Serializes this snapshot to a file. + * + * @param file the file to which this snapshot is serialized + * @param txtFile + * @throws IOException + */ + public void save(File file, File txtFile) throws IOException { + SnapshotProfilingInfo.SnapshotObjectOutputStream oos = new SnapshotObjectOutputStream(new FileOutputStream(file)); + try { + oos.writeObject(this); + } finally { + oos.close(); + } + if (txtFile != null) { + PrintStream out = new PrintStream(txtFile); + try { + out.println(CiUtil.profileToString(this, null, CiUtil.NEW_LINE)); + } finally { + out.close(); + } + } + } + + static class RiResolvedTypePlaceholder implements Serializable { + private static final long serialVersionUID = -5149982457010023916L; + final Class javaMirror; + public RiResolvedTypePlaceholder(Class javaMirror) { + this.javaMirror = javaMirror; + } + } + + static class SnapshotObjectOutputStream extends ObjectOutputStream { + public SnapshotObjectOutputStream(OutputStream out) throws IOException { + super(out); + enableReplaceObject(true); + } + + @Override + protected Object replaceObject(Object obj) throws IOException { + if (obj instanceof RiResolvedType) { + return new RiResolvedTypePlaceholder(((RiResolvedType) obj).toJava()); + } + return obj; + } + } + + static class SnapshotObjectInputStream extends ObjectInputStream { + private final RiRuntime runtime; + public SnapshotObjectInputStream(InputStream in, RiRuntime runtime) throws IOException { + super(in); + enableResolveObject(true); + this.runtime = runtime; + } + + @Override + protected Object resolveObject(Object obj) throws IOException { + if (obj instanceof SnapshotProfilingInfo.RiResolvedTypePlaceholder) { + RiResolvedType type = runtime.getType(((SnapshotProfilingInfo.RiResolvedTypePlaceholder) obj).javaMirror); + return type; + } + return obj; + } + } +}