/* * Copyright 2010-2015 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.jetbrains.kotlin.utils; import com.intellij.openapi.diagnostic.Logger; import org.jetbrains.annotations.NotNull; import java.io.PrintStream; import java.util.Arrays; import java.util.Stack; import java.util.concurrent.locks.ReentrantLock; public class Profiler { // The stack is synchronized here: this is intentional private static final ThreadLocal<Stack<Profiler>> PROFILERS = new ThreadLocal<Stack<Profiler>>() { @Override protected Stack<Profiler> initialValue() { return new Stack<>(); } }; private static final ReentrantLock OUT_LOCK = new ReentrantLock(); @NotNull public static Profiler create(@NotNull String name) { //noinspection UseOfSystemOutOrSystemErr return create(name, System.out); } @NotNull public static Profiler create(@NotNull String name, @NotNull PrintStream out) { return create(name, new PrintingLogger(out)); } @NotNull public static Profiler create(@NotNull String name, @NotNull Logger log) { Profiler profiler = new Profiler(name, log); PROFILERS.get().push(profiler); return profiler; } public static Profiler getFromContext() { Stack<Profiler> profilers = PROFILERS.get(); if (profilers.isEmpty()) { throw new UnsupportedOperationException(); } return profilers.peek(); } private final String name; private final Logger log; private long start = Long.MAX_VALUE; private long cumulative = 0; private boolean paused = true; private StackTraceElement[] stackTrace; private boolean mute; private String formatString; private Profiler(@NotNull String name, @NotNull Logger log) { this.name = name; this.log = log; setPrintAccuracy(3); } public Profiler recordStackTrace(int depth) { return recordStackTrace(1 /*skipping this frame*/, depth); } public Profiler recordStackTrace(int skip, int depth) { StackTraceElement[] trace = new Throwable().getStackTrace(); int from = 1 + skip; if (from >= trace.length) return this; int to; if (depth == -1) { to = trace.length; } else { to = Math.min(skip + depth + 1, trace.length); } stackTrace = Arrays.copyOfRange(trace, from, to); return this; } public Profiler resetStackTrace() { stackTrace = null; return this; } public Profiler printStackTrace() { if (stackTrace != null && log.isDebugEnabled()) { OUT_LOCK.lock(); try { for (StackTraceElement element : stackTrace) { println("\tat ", element); } } finally { OUT_LOCK.unlock(); } } return this; } public Profiler printEntering() { println("Entering ", name); return this; } public Profiler printThreadName() { println(Thread.currentThread().getName() + " ", name); return this; } public Profiler start() { if (paused) { start = System.nanoTime(); paused = false; } return this; } public Profiler end() { long result = cumulative; if (!paused) { result += System.nanoTime() - start; } paused = true; cumulative = 0; if (!mute && log.isDebugEnabled()) { OUT_LOCK.lock(); try { println(name, " took ", format(result)); printStackTrace(); } finally { OUT_LOCK.unlock(); } } return this; } public Profiler pause() { if (!paused) { cumulative += System.nanoTime() - start; paused = true; } return this; } public long getCumulative() { return cumulative; } public Profiler mute() { mute = true; return this; } public Profiler unmute() { mute = false; return this; } public Profiler println(Object message) { if (!mute && log.isDebugEnabled()) { log.debug(String.valueOf(message)); } return this; } public Profiler println(Object a, Object b) { if (!mute && log.isDebugEnabled()) { OUT_LOCK.lock(); try { log.debug(String.valueOf(a) + b); } finally { OUT_LOCK.unlock(); } } return this; } public Profiler println(Object a, Object b, Object c) { if (!mute && log.isDebugEnabled()) { OUT_LOCK.lock(); try { log.debug(String.valueOf(a) + b + c); } finally { OUT_LOCK.unlock(); } } return this; } public Profiler println(Object a, Object b, Object c, Object... rest) { if (!mute && log.isDebugEnabled()) { OUT_LOCK.lock(); try { StringBuilder sb = new StringBuilder(); sb.append(a); sb.append(b); sb.append(c); for (Object o : rest) { sb.append(o); } log.debug(sb.toString()); } finally { OUT_LOCK.unlock(); } } return this; } public Profiler setPrintAccuracy(int accuracy) { formatString = "%." + accuracy + "fs"; return this; } private String format(long delta) { return String.format(formatString, delta / 1e9); } }