001/*
002 * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package com.oracle.graal.hotspot;
024
025import static java.lang.Thread.*;
026
027import java.io.*;
028import java.lang.annotation.*;
029import java.lang.reflect.*;
030import java.util.*;
031import java.util.concurrent.*;
032
033import jdk.internal.jvmci.hotspot.*;
034
035import com.oracle.graal.debug.*;
036import com.sun.management.*;
037
038@SuppressWarnings("unused")
039public final class CompilationStatistics {
040
041    private static final long RESOLUTION = 100000000;
042    private static final boolean ENABLED = Boolean.getBoolean("jvmci.comp.stats");
043
044    private static final CompilationStatistics DUMMY = new CompilationStatistics(null, false);
045
046    private static ConcurrentLinkedDeque<CompilationStatistics> list = new ConcurrentLinkedDeque<>();
047
048    private static final ThreadLocal<Deque<CompilationStatistics>> current = new ThreadLocal<Deque<CompilationStatistics>>() {
049
050        @Override
051        protected Deque<CompilationStatistics> initialValue() {
052            return new ArrayDeque<>();
053        }
054    };
055
056    @Retention(RetentionPolicy.RUNTIME)
057    @Target(ElementType.FIELD)
058    private static @interface NotReported {
059    }
060
061    @Retention(RetentionPolicy.RUNTIME)
062    @Target(ElementType.FIELD)
063    private static @interface TimeValue {
064    }
065
066    private static long zeroTime = System.nanoTime();
067
068    private static long getThreadAllocatedBytes() {
069        ThreadMXBean thread = (ThreadMXBean) Management.getThreadMXBean();
070        return thread.getThreadAllocatedBytes(currentThread().getId());
071    }
072
073    @NotReported private final long startTime;
074    @NotReported private long threadAllocatedBytesStart;
075
076    private int bytecodeCount;
077    private int codeSize;
078    @TimeValue private long duration;
079    private long memoryUsed;
080    private final boolean osr;
081    private final String holder;
082    private final String name;
083    private final String signature;
084
085    private CompilationStatistics(HotSpotResolvedJavaMethod method, boolean osr) {
086        this.osr = osr;
087        if (method != null) {
088            holder = method.getDeclaringClass().getName();
089            name = method.getName();
090            signature = method.getSignature().toMethodDescriptor();
091            startTime = System.nanoTime();
092            bytecodeCount = method.getCodeSize();
093            threadAllocatedBytesStart = getThreadAllocatedBytes();
094        } else {
095            holder = "";
096            name = "";
097            signature = "";
098            startTime = 0;
099        }
100    }
101
102    public void finish(HotSpotResolvedJavaMethod method, HotSpotInstalledCode code) {
103        if (ENABLED) {
104            duration = System.nanoTime() - startTime;
105            codeSize = (int) code.getCodeSize();
106            memoryUsed = getThreadAllocatedBytes() - threadAllocatedBytesStart;
107            if (current.get().getLast() != this) {
108                throw new RuntimeException("mismatch in finish()");
109            }
110            current.get().removeLast();
111        }
112    }
113
114    public static CompilationStatistics current() {
115        return current.get().isEmpty() ? null : current.get().getLast();
116    }
117
118    public static CompilationStatistics create(HotSpotResolvedJavaMethod method, boolean isOSR) {
119        if (ENABLED) {
120            CompilationStatistics stats = new CompilationStatistics(method, isOSR);
121            list.add(stats);
122            current.get().addLast(stats);
123            return stats;
124        } else {
125            return DUMMY;
126        }
127    }
128
129    @SuppressWarnings("deprecation")
130    public static void clear(String dumpName) {
131        if (!ENABLED) {
132            return;
133        }
134        try {
135            ConcurrentLinkedDeque<CompilationStatistics> snapshot = list;
136            long snapshotZeroTime = zeroTime;
137
138            list = new ConcurrentLinkedDeque<>();
139            zeroTime = System.nanoTime();
140
141            Date now = new Date();
142            String dateString = (now.getYear() + 1900) + "-" + (now.getMonth() + 1) + "-" + now.getDate() + "-" + now.getHours() + "" + now.getMinutes();
143
144            dumpCompilations(snapshot, dumpName, dateString);
145
146            try (FileOutputStream fos = new FileOutputStream("timeline_" + dateString + "_" + dumpName + ".csv", true); PrintStream out = new PrintStream(fos)) {
147
148                long[] timeSpent = new long[10000];
149                int maxTick = 0;
150                for (CompilationStatistics stats : snapshot) {
151                    long start = stats.startTime - snapshotZeroTime;
152                    long duration = stats.duration;
153                    if (start < 0) {
154                        duration -= -start;
155                        start = 0;
156                    }
157
158                    int tick = (int) (start / RESOLUTION);
159                    long timeLeft = RESOLUTION - (start % RESOLUTION);
160
161                    while (tick < timeSpent.length && duration > 0) {
162                        if (tick > maxTick) {
163                            maxTick = tick;
164                        }
165                        timeSpent[tick] += Math.min(timeLeft, duration);
166                        duration -= timeLeft;
167                        tick++;
168                        timeLeft = RESOLUTION;
169                    }
170                }
171                String timelineName = System.getProperty("stats.timeline.name");
172                if (timelineName != null && !timelineName.isEmpty()) {
173                    out.print(timelineName + "\t");
174                }
175                for (int i = 0; i <= maxTick; i++) {
176                    out.print((timeSpent[i] * 100 / RESOLUTION) + "\t");
177                }
178                out.println();
179            }
180        } catch (Exception e) {
181            throw new RuntimeException(e);
182        }
183    }
184
185    protected static void dumpCompilations(ConcurrentLinkedDeque<CompilationStatistics> snapshot, String dumpName, String dateString) throws IllegalAccessException, FileNotFoundException {
186        String fileName = "compilations_" + dateString + "_" + dumpName + ".csv";
187        try (PrintStream out = new PrintStream(fileName)) {
188            // output the list of all compilations
189
190            Field[] declaredFields = CompilationStatistics.class.getDeclaredFields();
191            ArrayList<Field> fields = new ArrayList<>();
192            for (Field field : declaredFields) {
193                if (!Modifier.isStatic(field.getModifiers()) && !field.isAnnotationPresent(NotReported.class)) {
194                    fields.add(field);
195                }
196            }
197            for (Field field : fields) {
198                out.print(field.getName() + "\t");
199            }
200            out.println();
201            for (CompilationStatistics stats : snapshot) {
202                for (Field field : fields) {
203                    if (field.isAnnotationPresent(TimeValue.class)) {
204                        double value = field.getLong(stats) / 1000000d;
205                        out.print(String.format(Locale.ENGLISH, "%.3f", value) + "\t");
206                    } else {
207                        out.print(field.get(stats) + "\t");
208                    }
209                }
210                out.println();
211            }
212        }
213    }
214}