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 }