1/*
2 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23package org.graalvm.compiler.core;
24
25import static org.graalvm.compiler.core.GraalCompilerOptions.PrintCompilation;
26
27import org.graalvm.compiler.code.CompilationResult;
28import org.graalvm.compiler.core.common.CompilationIdentifier;
29import org.graalvm.compiler.debug.Management;
30import org.graalvm.compiler.debug.TTY;
31import org.graalvm.compiler.options.OptionValues;
32
33import jdk.vm.ci.meta.JavaMethod;
34import jdk.vm.ci.runtime.JVMCICompiler;
35
36/**
37 * Utility for printing an informational line to {@link TTY} upon completion of compiling a method.
38 */
39public final class CompilationPrinter {
40
41    private final CompilationIdentifier id;
42    private final JavaMethod method;
43    private final int entryBCI;
44    private final long start;
45    private final long allocatedBytesBefore;
46
47    /**
48     * Gets an object that will report statistics for a compilation if
49     * {@link GraalCompilerOptions#PrintCompilation} is enabled and {@link TTY} is not suppressed.
50     * This method should be called just before a compilation starts as it captures pre-compilation
51     * data for the purpose of {@linkplain #finish(CompilationResult) printing} the post-compilation
52     * statistics.
53     *
54     * @param options used to get the value of {@link GraalCompilerOptions#PrintCompilation}
55     * @param id the identifier for the compilation
56     * @param method the method for which code is being compiled
57     * @param entryBCI the BCI at which compilation starts
58     */
59    public static CompilationPrinter begin(OptionValues options, CompilationIdentifier id, JavaMethod method, int entryBCI) {
60        if (PrintCompilation.getValue(options) && !TTY.isSuppressed()) {
61            try {
62                Class.forName("java.lang.management.ManagementFactory");
63            } catch (ClassNotFoundException ex) {
64                throw new IllegalArgumentException("PrintCompilation option requires java.management module");
65            }
66            return new CompilationPrinter(id, method, entryBCI);
67        }
68        return DISABLED;
69    }
70
71    private static final CompilationPrinter DISABLED = new CompilationPrinter();
72
73    private CompilationPrinter() {
74        this.method = null;
75        this.id = null;
76        this.entryBCI = -1;
77        this.start = -1;
78        this.allocatedBytesBefore = -1;
79    }
80
81    private CompilationPrinter(CompilationIdentifier id, JavaMethod method, int entryBCI) {
82        this.method = method;
83        this.id = id;
84        this.entryBCI = entryBCI;
85
86        final long threadId = Thread.currentThread().getId();
87        start = System.nanoTime();
88        allocatedBytesBefore = getAllocatedBytes(threadId);
89    }
90
91    private String getMethodDescription() {
92        return String.format("%-30s %-70s %-45s %-50s %s", id.toString(CompilationIdentifier.Verbosity.ID),
93                        method.getDeclaringClass().getName(), method.getName(),
94                        method.getSignature().toMethodDescriptor(),
95                        entryBCI == JVMCICompiler.INVOCATION_ENTRY_BCI ? "" : "(OSR@" + entryBCI + ") ");
96    }
97
98    /**
99     * Notifies this object that the compilation finished and the informational line should be
100     * printed to {@link TTY}.
101     */
102    public void finish(CompilationResult result) {
103        if (id != null) {
104            final long threadId = Thread.currentThread().getId();
105            final long stop = System.nanoTime();
106            final long duration = (stop - start) / 1000000;
107            final int targetCodeSize = result != null ? result.getTargetCodeSize() : -1;
108            final int bytecodeSize = result != null ? result.getBytecodeSize() : 0;
109            final long allocatedBytesAfter = getAllocatedBytes(threadId);
110            final long allocatedKBytes = (allocatedBytesAfter - allocatedBytesBefore) / 1024;
111
112            TTY.println(getMethodDescription() + String.format(" | %4dms %5dB %5dB %5dkB", duration, bytecodeSize, targetCodeSize, allocatedKBytes));
113        }
114    }
115
116    static com.sun.management.ThreadMXBean threadMXBean;
117
118    static long getAllocatedBytes(long threadId) {
119        if (threadMXBean == null) {
120            threadMXBean = (com.sun.management.ThreadMXBean) Management.getThreadMXBean();
121        }
122        return threadMXBean.getThreadAllocatedBytes(threadId);
123    }
124}
125