/******************************************************************************* * Copyright 2013 SAP AG * * 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.sap.core.odata.core.debug; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import com.sap.core.odata.api.processor.ODataContext.RuntimeMeasurement; import com.sap.core.odata.core.ep.util.JsonStreamWriter; /** * @author SAP AG */ 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>(); public long memoryStarted; public 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") .unquotedValue(node.timeStopped == 0 ? null : Long.toString((node.timeStopped - node.timeStarted) / 1000)) .separator() .name("memory") .unquotedValue(node.memoryStopped == 0 ? null : Long.toString((node.memoryStopped - node.memoryStarted) / 1000)) .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(); } }