/*
* Copyright 2003-2011 JetBrains s.r.o.
*
* 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 jetbrains.mps.util.performance;
import org.jetbrains.annotations.NotNull;
import java.util.*;
/**
* Evgeny Gryaznov, Feb 23, 2010
*/
public class PerformanceTracer implements IPerformanceTracer {
public static final boolean IS_TRACING = true;
private int top = 0;
private StackElement[] myStack = new StackElement[4096];
private String traceName;
private List<String> externalText;
public PerformanceTracer(String name) {
this.traceName = name;
for (int i = 0; i < myStack.length; i++) {
myStack[i] = new StackElement();
}
for (int i = 0; i < precreated.length; i++) {
precreated[i] = new Task("default");
}
myStack[0].name = "Zero [" + name + "]";
myStack[0].task = new Task(myStack[0].name);
externalText = new ArrayList<String>();
}
@Override
public void push(String taskName, boolean isMajor) {
top++;
myStack[top].isMajor = isMajor;
myStack[top].name = isMajor ? "[" + taskName + "]" : taskName;
myStack[top].correction = 0;
myStack[top].startTime = System.nanoTime();
}
@Override
public void pop() {
long len = System.nanoTime() - myStack[top].startTime;
long correction = myStack[top].correction;
if (myStack[top].isMajor) {
for (int i = 0; i < top; i++) {
myStack[i].correction += len - correction;
}
}
String name = myStack[top].name;
Task wasTask = myStack[top].task;
// propagate leaves
if (!myStack[top].children.isEmpty()) {
if (wasTask == null) {
wasTask = getTask(top);
}
wasTask.tasks.addAll(myStack[top].children.values());
myStack[top].children.clear();
}
if (wasTask == null) {
myStack[top - 1].addLeaf(name, len, correction);
} else {
wasTask.executionTime += len;
wasTask.correction += correction;
}
myStack[top].task = null;
top--;
}
private Task getTask(int i) {
Task t = myStack[i].task;
if (t == null) {
t = createFast(myStack[i].name);
myStack[i].task = t;
Task parent = getTask(i - 1);
parent.addTask(t);
}
return t;
}
@Override
public void addText(String s) {
externalText.add(s);
}
@Override
public String report(String... separate) {
if (top == 0) {
myStack[0].task.tasks.addAll(myStack[0].children.values());
myStack[0].task.merge(new HashSet<>(Arrays.asList(separate)));
StringBuilder sb = new StringBuilder();
sb.append("[");
sb.append(traceName);
sb.append("]\n");
myStack[0].task.toString(sb, 0);
for (String s : externalText) {
sb.append(s);
sb.append("\n");
}
return sb.toString();
} else {
return null;
}
}
private class StackElement {
String name;
long startTime;
long correction;
Task task;
boolean isMajor;
Map<String, Task> children = new HashMap<String, Task>();
private Task addLeaf(String name, long time, long correction) {
Task t = children.get(name);
if (t == null) {
t = createFast(name);
children.put(name, t);
} else {
t.invocationCount++;
}
t.executionTime += time;
t.correction += correction;
return t;
}
}
private static class Task implements Comparable<Task> {
String name;
long executionTime;
long correction;
int invocationCount;
List<Task> tasks = new LinkedList<Task>();
private Task(String name) {
this.name = name;
this.executionTime = 0;
this.invocationCount = 1;
}
private void addTask(Task task) {
tasks.add(task);
}
public void merge(Set<String> keepUnmerged) {
Map<String, Task> map = new HashMap<String, Task>();
Iterator<Task> it = tasks.iterator();
while (it.hasNext()) {
Task n = it.next();
if (keepUnmerged.contains(n.name)) {
continue;
}
Task prev = map.get(n.name);
if (prev == null) {
map.put(n.name, n);
} else {
it.remove();
prev.mergeWith(n);
}
}
for (Task t : tasks) {
t.merge(keepUnmerged);
}
}
private void mergeWith(Task n) {
executionTime += n.executionTime;
invocationCount += n.invocationCount;
correction += n.correction;
tasks.addAll(n.tasks);
}
public void toString(StringBuilder sb, int indent) {
if (name != null) {
for (int i = 0; i < indent; i++) {
sb.append(" ");
}
sb.append(name);
sb.append(':');
sb.append(invocationCount);
sb.append(": ");
sb.append(executionTime / 1000000.);
sb.append(" ms");
if (correction != 0) {
sb.append(" (real: ");
sb.append((executionTime - correction) / 1000000.);
sb.append(" ms)");
}
sb.append("\n");
}
Collections.sort(tasks);
for (Task t : tasks) {
t.toString(sb, name == null ? indent : indent + 2);
}
}
@Override
public int compareTo(@NotNull Task task) {
return Long.valueOf(task.executionTime).compareTo(executionTime);
}
}
private Task createFast(String name) {
if (precreatedIndex < precreated.length) {
Task t = precreated[precreatedIndex++];
t.name = name;
return t;
} else {
return new Task(name);
}
}
private Task[] precreated = new Task[4096]; // todo: AP what is that?
int precreatedIndex = 0;
}