blob: 1b718cabc106cf890e417810fdf13a860f099c7a [file] [log] [blame]
/*
* Copyright 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.dev.util;
import java.util.ArrayList;
import java.util.List;
/**
* Performs basic recording/logging of performance metrics for internal
* development purposes.
*
* <p>
* This class differs from {@link com.google.gwt.core.ext.TreeLogger TreeLogger}
* by providing an interface more suitable for metrics-oriented logging.
* </p>
*
* <p>
* Performance logging can be enabled by setting the system property,
* {@code gwt.perflog=true}.
* </p>
*
*/
public class PerfLogger {
private static class Timing {
String message;
long startTimeNanos;
long totalTimeNanos;
Timing parent = null;
List<Timing> subTimings = new ArrayList<Timing>();
boolean messageOnly;
Timing(Timing parent, String message) {
this.parent = parent;
this.message = message;
this.startTimeNanos = System.nanoTime();
}
Timing() {
}
boolean isRoot() {
return parent == null;
}
}
/**
* Flag for enabling performance logging.
*/
private static boolean enabled = Boolean.parseBoolean(System.getProperty("gwt.perflog"));
private static ThreadLocal<Timing> currentTiming = new ThreadLocal<Timing>() {
@Override
protected Timing initialValue() {
return new Timing();
}
};
/**
* Ends the current timing.
*/
public static void end() {
if (!enabled) {
return;
}
long endTimeNanos = System.nanoTime();
Timing timing = currentTiming.get();
if (timing.isRoot()) {
System.out.println("Tried to end a timing that was never started!\n");
return;
}
timing.totalTimeNanos = endTimeNanos - timing.startTimeNanos;
Timing newCurrent = timing.parent;
currentTiming.set(newCurrent);
if (newCurrent.isRoot()) {
printTimings();
newCurrent.subTimings = new ArrayList<Timing>();
}
}
/**
* Logs a message without explicitly including timer information.
*
* @param message a not <code>null</code> message
*/
public static void log(String message) {
if (enabled) {
start(message);
currentTiming.get().messageOnly = true;
end();
}
}
/**
* Starts a new timing corresponding to {@code message}. You must call {@link
* #end} for each corresponding call to {@code start}. You may nest timing
* calls.
*
* @param message a not <code>null</code> message.
*/
public static void start(String message) {
if (!enabled) {
return;
}
Timing current = currentTiming.get();
Timing newTiming = new Timing(current, message);
current.subTimings.add(newTiming);
currentTiming.set(newTiming);
}
private static String getIndentString(int level) {
StringBuffer str = new StringBuffer(level * 2);
for (int i = 0; i < level; ++i) {
str.append(" ");
}
return str.toString();
}
private static void printTiming(Timing t, int depth) {
if (!t.isRoot()) {
StringBuffer msg = new StringBuffer(getIndentString(depth - 1));
msg.append("[perf] ");
msg.append(t.message);
if (!t.messageOnly) {
msg.append(" ");
msg.append(t.totalTimeNanos / 1000000);
msg.append("ms");
}
System.out.println(msg);
}
++depth;
for (Timing subTiming : t.subTimings) {
printTiming(subTiming, depth);
}
}
private static void printTimings() {
printTiming(currentTiming.get(), 0);
}
}