blob: e0a9ca38ef89bd9183d1d93d1f48d8dbf36a6dba [file] [log] [blame]
/*******************************************************************************
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.olingo.odata2.core.debug;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.olingo.odata2.api.processor.ODataContext.RuntimeMeasurement;
import org.apache.olingo.odata2.core.ep.util.JsonStreamWriter;
/**
* Runtime debug information.
*/
public class DebugInfoRuntime implements DebugInfo {
private class RuntimeNode {
protected String className;
protected String methodName;
protected long timeStarted;
protected long timeStopped;
protected List<RuntimeNode> children = new ArrayList<RuntimeNode>();
protected long memoryStarted;
protected long memoryStopped;
protected RuntimeNode() {
timeStarted = 0;
timeStopped = Long.MAX_VALUE;
memoryStarted = 0;
memoryStopped = 0;
}
private RuntimeNode(final RuntimeMeasurement runtimeMeasurement) {
className = runtimeMeasurement.getClassName();
methodName = runtimeMeasurement.getMethodName();
timeStarted = runtimeMeasurement.getTimeStarted();
timeStopped = runtimeMeasurement.getTimeStopped();
memoryStarted = runtimeMeasurement.getMemoryStarted();
memoryStopped = runtimeMeasurement.getMemoryStopped();
}
protected boolean add(final RuntimeMeasurement runtimeMeasurement) {
if (timeStarted <= runtimeMeasurement.getTimeStarted()
&& timeStopped != 0 && timeStopped >= runtimeMeasurement.getTimeStopped()) {
for (RuntimeNode candidate : children) {
if (candidate.add(runtimeMeasurement)) {
return true;
}
}
children.add(new RuntimeNode(runtimeMeasurement));
return true;
} else {
return false;
}
}
/**
* Combines runtime measurements with identical class names and method
* names into one measurement, assuming that they originate from a loop
* or a similar construct where a summary measurement has been intended.
*/
protected void combineRuntimeMeasurements() {
RuntimeNode preceding = null;
for (Iterator<RuntimeNode> iterator = children.iterator(); iterator.hasNext();) {
final RuntimeNode child = iterator.next();
if (preceding != null
&& preceding.timeStopped != 0 && child.timeStopped != 0
&& preceding.timeStopped <= child.timeStarted
&& preceding.children.isEmpty() && child.children.isEmpty()
&& preceding.methodName.equals(child.methodName)
&& preceding.className.equals(child.className)) {
preceding.timeStarted = child.timeStarted - (preceding.timeStopped - preceding.timeStarted);
preceding.timeStopped = child.timeStopped;
preceding.memoryStarted = child.memoryStarted - (preceding.memoryStopped - preceding.memoryStarted);
preceding.memoryStopped = child.memoryStopped;
iterator.remove();
} else {
preceding = child;
child.combineRuntimeMeasurements();
}
}
}
}
private final RuntimeNode rootNode;
public DebugInfoRuntime(final List<RuntimeMeasurement> runtimeMeasurements) {
rootNode = new RuntimeNode();
for (final RuntimeMeasurement runtimeMeasurement : runtimeMeasurements) {
rootNode.add(runtimeMeasurement);
}
rootNode.combineRuntimeMeasurements();
}
@Override
public String getName() {
return "Runtime";
}
@Override
public void appendJson(final JsonStreamWriter jsonStreamWriter) throws IOException {
appendJsonChildren(jsonStreamWriter, rootNode);
}
private static void appendJsonNode(final JsonStreamWriter jsonStreamWriter, final RuntimeNode node)
throws IOException {
jsonStreamWriter.beginObject()
.namedStringValueRaw("class", node.className).separator()
.namedStringValueRaw("method", node.methodName).separator()
.name("duration");
if (node.timeStopped == 0) {
jsonStreamWriter.unquotedValue(null);
} else {
jsonStreamWriter.beginObject()
.name("value").unquotedValue(Long.toString((node.timeStopped - node.timeStarted) / 1000)).separator()
.namedStringValueRaw("unit", "µs")
.endObject();
}
if (!node.children.isEmpty()) {
jsonStreamWriter.separator()
.name("children");
appendJsonChildren(jsonStreamWriter, node);
}
jsonStreamWriter.endObject();
}
private static void appendJsonChildren(final JsonStreamWriter jsonStreamWriter, final RuntimeNode node)
throws IOException {
jsonStreamWriter.beginArray();
boolean first = true;
for (final RuntimeNode childNode : node.children) {
if (!first) {
jsonStreamWriter.separator();
}
first = false;
appendJsonNode(jsonStreamWriter, childNode);
}
jsonStreamWriter.endArray();
}
@Override
public void appendHtml(final Writer writer) throws IOException {
appendRuntimeNode(rootNode, "", true, writer);
}
private void appendRuntimeNode(final RuntimeNode node, final String draw, final boolean isLast, final Writer writer)
throws IOException {
if (node.className != null) {
writer.append("<li>")
.append("<span class=\"code\">")
.append("<span class=\"draw\">").append(draw)
.append(isLast ? "&#x2514;" : "&#x251C;").append("&#x2500;&nbsp;</span>")
.append("<span class=\"class\">").append(node.className).append("</span>.")
.append("<span class=\"method\">").append(node.methodName).append("(&hellip;)")
.append("</span></span>");
long time = node.timeStopped == 0 ? 0 : (node.timeStopped - node.timeStarted) / 1000;
writer.append("<span class=\"").append(time == 0 ? "null" : "numeric")
.append("\" title=\"").append(time == 0 ? "Stop time missing" : "Gross duration")
.append("\">").append(time == 0 ? "unfinished" : Long.toString(time) + "&nbsp;&micro;s")
.append("</span>\n");
}
if (!node.children.isEmpty()) {
writer.append("<ol class=\"tree\">\n");
for (final RuntimeNode childNode : node.children) {
appendRuntimeNode(childNode,
node.className == null ? draw : draw + (isLast ? "&nbsp;" : "&#x2502;") + "&nbsp;&nbsp;",
node.children.indexOf(childNode) == node.children.size() - 1,
writer);
}
writer.append("</ol>\n");
}
if (node.className != null) {
writer.append("</li>\n");
}
}
}