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 }