001 /* 002 * Copyright 2010-2015 JetBrains s.r.o. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017 package org.jetbrains.kotlin.utils; 018 019 import com.intellij.openapi.diagnostic.Logger; 020 import org.jetbrains.annotations.NotNull; 021 022 import java.io.PrintStream; 023 import java.util.Arrays; 024 import java.util.Stack; 025 import java.util.concurrent.locks.ReentrantLock; 026 027 public class Profiler { 028 // The stack is synchronized here: this is intentional 029 private static final ThreadLocal<Stack<Profiler>> PROFILERS = new ThreadLocal<Stack<Profiler>>() { 030 @Override 031 protected Stack<Profiler> initialValue() { 032 return new Stack<Profiler>(); 033 } 034 }; 035 036 private static final ReentrantLock OUT_LOCK = new ReentrantLock(); 037 038 @NotNull 039 public static Profiler create(@NotNull String name) { 040 //noinspection UseOfSystemOutOrSystemErr 041 return create(name, System.out); 042 } 043 044 @NotNull 045 public static Profiler create(@NotNull String name, @NotNull PrintStream out) { 046 return create(name, new PrintingLogger(out)); 047 } 048 049 @NotNull 050 public static Profiler create(@NotNull String name, @NotNull Logger log) { 051 Profiler profiler = new Profiler(name, log); 052 PROFILERS.get().push(profiler); 053 return profiler; 054 } 055 056 public static Profiler getFromContext() { 057 Stack<Profiler> profilers = PROFILERS.get(); 058 if (profilers.isEmpty()) { 059 throw new UnsupportedOperationException(); 060 } 061 return profilers.peek(); 062 } 063 064 private final String name; 065 private final Logger log; 066 private long start = Long.MAX_VALUE; 067 private long cumulative = 0; 068 private boolean paused = true; 069 private StackTraceElement[] stackTrace; 070 private boolean mute; 071 072 private Profiler(@NotNull String name, @NotNull Logger log) { 073 this.name = name; 074 this.log = log; 075 } 076 077 public Profiler recordStackTrace(int depth) { 078 return recordStackTrace(1 /*skipping this frame*/, depth); 079 } 080 081 public Profiler recordStackTrace(int skip, int depth) { 082 StackTraceElement[] trace = new Throwable().getStackTrace(); 083 084 int from = 1 + skip; 085 if (from >= trace.length) return this; 086 087 int to; 088 if (depth == -1) { 089 to = trace.length; 090 } 091 else { 092 to = Math.min(skip + depth + 1, trace.length); 093 } 094 095 stackTrace = Arrays.copyOfRange(trace, from, to); 096 return this; 097 } 098 099 public Profiler resetStackTrace() { 100 stackTrace = null; 101 return this; 102 } 103 104 public Profiler printStackTrace() { 105 if (stackTrace != null && log.isDebugEnabled()) { 106 OUT_LOCK.lock(); 107 try { 108 for (StackTraceElement element : stackTrace) { 109 println("\tat ", element); 110 } 111 } 112 finally { 113 OUT_LOCK.unlock(); 114 } 115 } 116 return this; 117 } 118 119 public Profiler printEntering() { 120 println("Entering ", name); 121 return this; 122 } 123 124 public Profiler start() { 125 if (paused) { 126 start = System.nanoTime(); 127 paused = false; 128 } 129 return this; 130 } 131 132 public Profiler end() { 133 long result = cumulative; 134 if (!paused) { 135 result += System.nanoTime() - start; 136 } 137 paused = true; 138 cumulative = 0; 139 140 if (log.isDebugEnabled()) { 141 OUT_LOCK.lock(); 142 try { 143 println(name, " took ", format(result)); 144 printStackTrace(); 145 } 146 finally { 147 OUT_LOCK.unlock(); 148 } 149 } 150 151 return this; 152 } 153 154 public Profiler pause() { 155 if (!paused) { 156 cumulative += System.nanoTime() - start; 157 paused = true; 158 } 159 return this; 160 } 161 162 public Profiler mute() { 163 mute = true; 164 return this; 165 } 166 167 public Profiler unmute() { 168 mute = false; 169 return this; 170 } 171 172 public Profiler println(Object message) { 173 if (!mute && log.isDebugEnabled()) { 174 log.debug(String.valueOf(message)); 175 } 176 return this; 177 } 178 179 public Profiler println(Object a, Object b) { 180 if (!mute && log.isDebugEnabled()) { 181 OUT_LOCK.lock(); 182 try { 183 log.debug(String.valueOf(a) + b); 184 } 185 finally { 186 OUT_LOCK.unlock(); 187 } 188 } 189 return this; 190 } 191 192 public Profiler println(Object a, Object b, Object c) { 193 if (!mute && log.isDebugEnabled()) { 194 OUT_LOCK.lock(); 195 try { 196 log.debug(String.valueOf(a) + b + c); 197 } 198 finally { 199 OUT_LOCK.unlock(); 200 } 201 } 202 return this; 203 } 204 205 public Profiler println(Object a, Object b, Object c, Object... rest) { 206 if (!mute && log.isDebugEnabled()) { 207 OUT_LOCK.lock(); 208 try { 209 StringBuilder sb = new StringBuilder(); 210 sb.append(a); 211 sb.append(b); 212 sb.append(c); 213 for (Object o : rest) { 214 sb.append(o); 215 } 216 log.debug(sb.toString()); 217 } 218 finally { 219 OUT_LOCK.unlock(); 220 } 221 } 222 return this; 223 } 224 225 private static String format(long delta) { 226 return String.format("%.3fs", delta / 1e9); 227 } 228 }