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    }