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 private String formatString; 072 073 private Profiler(@NotNull String name, @NotNull Logger log) { 074 this.name = name; 075 this.log = log; 076 setPrintAccuracy(3); 077 } 078 079 public Profiler recordStackTrace(int depth) { 080 return recordStackTrace(1 /*skipping this frame*/, depth); 081 } 082 083 public Profiler recordStackTrace(int skip, int depth) { 084 StackTraceElement[] trace = new Throwable().getStackTrace(); 085 086 int from = 1 + skip; 087 if (from >= trace.length) return this; 088 089 int to; 090 if (depth == -1) { 091 to = trace.length; 092 } 093 else { 094 to = Math.min(skip + depth + 1, trace.length); 095 } 096 097 stackTrace = Arrays.copyOfRange(trace, from, to); 098 return this; 099 } 100 101 public Profiler resetStackTrace() { 102 stackTrace = null; 103 return this; 104 } 105 106 public Profiler printStackTrace() { 107 if (stackTrace != null && log.isDebugEnabled()) { 108 OUT_LOCK.lock(); 109 try { 110 for (StackTraceElement element : stackTrace) { 111 println("\tat ", element); 112 } 113 } 114 finally { 115 OUT_LOCK.unlock(); 116 } 117 } 118 return this; 119 } 120 121 public Profiler printEntering() { 122 println("Entering ", name); 123 return this; 124 } 125 126 public Profiler printThreadName() { 127 println(Thread.currentThread().getName() + " ", name); 128 return this; 129 } 130 131 public Profiler start() { 132 if (paused) { 133 start = System.nanoTime(); 134 paused = false; 135 } 136 return this; 137 } 138 139 public Profiler end() { 140 long result = cumulative; 141 if (!paused) { 142 result += System.nanoTime() - start; 143 } 144 paused = true; 145 cumulative = 0; 146 147 if (!mute && log.isDebugEnabled()) { 148 OUT_LOCK.lock(); 149 try { 150 println(name, " took ", format(result)); 151 printStackTrace(); 152 } 153 finally { 154 OUT_LOCK.unlock(); 155 } 156 } 157 158 return this; 159 } 160 161 public Profiler pause() { 162 if (!paused) { 163 cumulative += System.nanoTime() - start; 164 paused = true; 165 } 166 return this; 167 } 168 169 public long getCumulative() { 170 return cumulative; 171 } 172 173 public Profiler mute() { 174 mute = true; 175 return this; 176 } 177 178 public Profiler unmute() { 179 mute = false; 180 return this; 181 } 182 183 public Profiler println(Object message) { 184 if (!mute && log.isDebugEnabled()) { 185 log.debug(String.valueOf(message)); 186 } 187 return this; 188 } 189 190 public Profiler println(Object a, Object b) { 191 if (!mute && log.isDebugEnabled()) { 192 OUT_LOCK.lock(); 193 try { 194 log.debug(String.valueOf(a) + b); 195 } 196 finally { 197 OUT_LOCK.unlock(); 198 } 199 } 200 return this; 201 } 202 203 public Profiler println(Object a, Object b, Object c) { 204 if (!mute && log.isDebugEnabled()) { 205 OUT_LOCK.lock(); 206 try { 207 log.debug(String.valueOf(a) + b + c); 208 } 209 finally { 210 OUT_LOCK.unlock(); 211 } 212 } 213 return this; 214 } 215 216 public Profiler println(Object a, Object b, Object c, Object... rest) { 217 if (!mute && log.isDebugEnabled()) { 218 OUT_LOCK.lock(); 219 try { 220 StringBuilder sb = new StringBuilder(); 221 sb.append(a); 222 sb.append(b); 223 sb.append(c); 224 for (Object o : rest) { 225 sb.append(o); 226 } 227 log.debug(sb.toString()); 228 } 229 finally { 230 OUT_LOCK.unlock(); 231 } 232 } 233 return this; 234 } 235 236 public Profiler setPrintAccuracy(int accuracy) { 237 formatString = "%." + accuracy + "fs"; 238 return this; 239 } 240 241 private String format(long delta) { 242 return String.format(formatString, delta / 1e9); 243 } 244 }