001 /*
002 * Copyright 2010-2012 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.jet.utils;
018
019 import org.jetbrains.annotations.NotNull;
020 import org.jetbrains.annotations.Nullable;
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 return create(name, null);
041 }
042
043 @NotNull
044 public static Profiler create(@NotNull String name, @Nullable PrintStream out) {
045 Profiler profiler = new Profiler(name, out);
046 PROFILERS.get().push(profiler);
047 return profiler;
048 }
049
050 public static Profiler getFromContext() {
051 Stack<Profiler> profilers = PROFILERS.get();
052 if (profilers.isEmpty()) {
053 throw new UnsupportedOperationException();
054 }
055 return profilers.peek();
056 }
057
058 private final String name;
059 private final PrintStream out;
060 private long start = Long.MAX_VALUE;
061 private long cumulative = 0;
062 private boolean paused = true;
063 private StackTraceElement[] stackTrace;
064 private boolean mute;
065
066 private Profiler(@NotNull String name, @Nullable PrintStream out) {
067 this.name = name;
068 this.out = out == null ? System.out : out;
069 }
070
071 public Profiler recordStackTrace(int depth) {
072 return recordStackTrace(1 /*skipping this frame*/, depth);
073 }
074
075 public Profiler recordStackTrace(int skip, int depth) {
076 StackTraceElement[] trace = new Throwable().getStackTrace();
077
078 int from = 1 + skip;
079 if (from >= trace.length) return this;
080
081 int to;
082 if (depth == -1) {
083 to = trace.length;
084 }
085 else {
086 to = Math.min(skip + depth + 1, trace.length);
087 }
088
089 stackTrace = Arrays.copyOfRange(trace, from, to);
090 return this;
091 }
092
093 public Profiler resetStackTrace() {
094 stackTrace = null;
095 return this;
096 }
097
098 public Profiler printStackTrace() {
099 if (stackTrace != null) {
100 OUT_LOCK.lock();
101 try {
102 for (StackTraceElement element : stackTrace) {
103 println("\tat ", element);
104 }
105 }
106 finally {
107 OUT_LOCK.unlock();
108 }
109 }
110 return this;
111 }
112
113 public Profiler printEntering() {
114 println("Entering ", name);
115 return this;
116 }
117
118 public Profiler start() {
119 if (paused) {
120 start = System.nanoTime();
121 paused = false;
122 }
123 return this;
124 }
125
126 public Profiler end() {
127 long result = cumulative;
128 if (!paused) {
129 result += System.nanoTime() - start;
130 }
131 paused = true;
132 cumulative = 0;
133
134 OUT_LOCK.lock();
135 try {
136 println(name, " took ", format(result));
137 printStackTrace();
138 }
139 finally {
140 OUT_LOCK.unlock();
141 }
142
143 return this;
144 }
145
146 public Profiler pause() {
147 if (!paused) {
148 cumulative += System.nanoTime() - start;
149 paused = true;
150 }
151 return this;
152 }
153
154 public Profiler mute() {
155 mute = true;
156 return this;
157 }
158
159 public Profiler unmute() {
160 mute = false;
161 return this;
162 }
163
164 public Profiler println(Object message) {
165 if (!mute) {
166 out.println(message);
167 }
168 return this;
169 }
170
171 public Profiler println(Object a, Object b) {
172 if (!mute) {
173 OUT_LOCK.lock();
174 try {
175 out.print(a);
176 out.println(b);
177 }
178 finally {
179 OUT_LOCK.unlock();
180 }
181 }
182 return this;
183 }
184
185 public Profiler println(Object a, Object b, Object c) {
186 if (!mute) {
187 OUT_LOCK.lock();
188 try {
189 out.print(a);
190 out.print(b);
191 out.println(c);
192 }
193 finally {
194 OUT_LOCK.unlock();
195 }
196 }
197 return this;
198 }
199
200 public Profiler println(Object a, Object b, Object c, Object... rest) {
201 if (!mute) {
202 OUT_LOCK.lock();
203 try {
204 out.print(a);
205 out.print(b);
206 out.print(c);
207 for (Object o : rest) {
208 out.print(o);
209 }
210 out.println();
211 }
212 finally {
213 OUT_LOCK.unlock();
214 }
215 }
216 return this;
217 }
218
219 private static String format(long delta) {
220 return String.format("%.3fs", delta / 1e9);
221 }
222 }