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 printThreadName() { 125 println(Thread.currentThread().getName() + " ", name); 126 return this; 127 } 128 129 public Profiler start() { 130 if (paused) { 131 start = System.nanoTime(); 132 paused = false; 133 } 134 return this; 135 } 136 137 public Profiler end() { 138 long result = cumulative; 139 if (!paused) { 140 result += System.nanoTime() - start; 141 } 142 paused = true; 143 cumulative = 0; 144 145 if (!mute && log.isDebugEnabled()) { 146 OUT_LOCK.lock(); 147 try { 148 println(name, " took ", format(result)); 149 printStackTrace(); 150 } 151 finally { 152 OUT_LOCK.unlock(); 153 } 154 } 155 156 return this; 157 } 158 159 public Profiler pause() { 160 if (!paused) { 161 cumulative += System.nanoTime() - start; 162 paused = true; 163 } 164 return this; 165 } 166 167 public Profiler mute() { 168 mute = true; 169 return this; 170 } 171 172 public Profiler unmute() { 173 mute = false; 174 return this; 175 } 176 177 public Profiler println(Object message) { 178 if (!mute && log.isDebugEnabled()) { 179 log.debug(String.valueOf(message)); 180 } 181 return this; 182 } 183 184 public Profiler println(Object a, Object b) { 185 if (!mute && log.isDebugEnabled()) { 186 OUT_LOCK.lock(); 187 try { 188 log.debug(String.valueOf(a) + b); 189 } 190 finally { 191 OUT_LOCK.unlock(); 192 } 193 } 194 return this; 195 } 196 197 public Profiler println(Object a, Object b, Object c) { 198 if (!mute && log.isDebugEnabled()) { 199 OUT_LOCK.lock(); 200 try { 201 log.debug(String.valueOf(a) + b + c); 202 } 203 finally { 204 OUT_LOCK.unlock(); 205 } 206 } 207 return this; 208 } 209 210 public Profiler println(Object a, Object b, Object c, Object... rest) { 211 if (!mute && log.isDebugEnabled()) { 212 OUT_LOCK.lock(); 213 try { 214 StringBuilder sb = new StringBuilder(); 215 sb.append(a); 216 sb.append(b); 217 sb.append(c); 218 for (Object o : rest) { 219 sb.append(o); 220 } 221 log.debug(sb.toString()); 222 } 223 finally { 224 OUT_LOCK.unlock(); 225 } 226 } 227 return this; 228 } 229 230 private static String format(long delta) { 231 return String.format("%.3fs", delta / 1e9); 232 } 233 }