/** * Copyright 2014 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 org.spotter.ext.detection.est; import java.util.ArrayList; import java.util.List; /** * Aggregated trace. Groups loops, and calculates average over equally * structured simple traces. * * @author Alexander Wert * */ public class AggTrace { public static final String LOOP_STR = "LOOP"; // private static final long PER_CENT = 100; private List<AggTrace> subTraces; private AggTrace parent; private String methodName; private boolean loop; private int loopCount; private boolean sendMethod; private long overhead; private long payload; /** * Constructor. */ public AggTrace() { setParent(null); } /** * Constructor. * * @param parent * parent method */ public AggTrace(AggTrace parent) { setParent(parent); } /** * Constructor. * * @param methodName * name of the current method */ public AggTrace(String methodName) { setParent(null); setMethodName(methodName); } /** * Constructor. * * @param methodName * name of the current method * @param parent * parent method */ public AggTrace(AggTrace parent, String methodName) { setParent(parent); setMethodName(methodName); } /** * @return the subTraces */ public List<AggTrace> getSubTraces() { if (subTraces == null) { subTraces = new ArrayList<>(); } return subTraces; } /** * @return the methodName */ public String getMethodName() { return methodName; } /** * @param methodName * the methodName to set */ public void setMethodName(String methodName) { this.methodName = methodName; } /** * The path in the trace to the root node. * * @return path to the parent as string */ public String getPathToParentString() { String result = ""; AggTrace currentTrace = this; int depth = -1; while (currentTrace != null) { depth++; currentTrace = currentTrace.getParent(); } currentTrace = this; int currentDepth = depth; while (currentTrace != null) { String indention = ""; for (int i = 1; i <= currentDepth; i++) { indention += " "; } result += indention + currentTrace.getMethodName(); if (currentTrace.isLoop()) { result += " [" + currentTrace.getLoopCount() + "]"; } result += "\n"; currentTrace = currentTrace.getParent(); currentDepth--; } return result; } /** * @return the parent */ public AggTrace getParent() { return parent; } /** * @param parent * the parent to set */ public void setParent(AggTrace parent) { this.parent = parent; if (parent != null && !parent.getSubTraces().contains(this)) { parent.getSubTraces().add(this); } } /** * @return the loop */ public boolean isLoop() { return loop; } /** * @param loop * the loop to set */ public void setLoop(boolean loop) { this.loop = loop; } /** * @return the loopCount */ public int getLoopCount() { return loopCount; } /** * @param loopCount * the loopCount to set */ public void setLoopCount(int loopCount) { this.loopCount = loopCount; } @Override public String toString() { int depth = 0; AggTrace parent = getParent(); while (parent != null) { depth++; parent = parent.getParent(); } StringBuilder indention = new StringBuilder(); for (int i = 0; i < depth; i++) { indention.append(" "); } StringBuilder strBuilder = new StringBuilder(); strBuilder.append(indention.toString()); strBuilder.append(getMethodName()); if (isLoop()) { strBuilder.append(" ["); strBuilder.append(getLoopCount()); strBuilder.append("]"); } else if (isSendMethod()) { strBuilder.append(" ***SENT: "); strBuilder.append(getOverhead()); strBuilder.append(" | "); strBuilder.append(getPayload()); strBuilder.append(" Bytes"); } strBuilder.append("\n"); for (AggTrace child : getSubTraces()) { strBuilder.append(child.toString()); } return strBuilder.toString(); } /** * * @param other * trace to compare with * @return returns true if other trace represents the same operation * sequence */ public boolean similarTrace(Trace other) { if (!getMethodName().equals(other.getMethodName())) { return false; } for (int i = 0; i < getSubTraces().size(); i++) { if (!getSubTraces().get(i).similarTrace(other.getSubTraces().get(i))) { return false; } } return true; } /** * Transforms a simple trace to an aggregated trace (grouping up loops). * * @param rootTrace * root trace to convert * @return aggreagated trace */ public static AggTrace fromTrace(Trace rootTrace) { if (rootTrace == null) { return null; } if (rootTrace.getParent() != null) { throw new IllegalArgumentException( "Cannot convert trace which is not a root (parent not null) into an aggregated trace."); } return fromTrace(rootTrace, null); } private static AggTrace fromTrace(Trace trace, AggTrace parentAggTrace) { AggTrace aggRootTrace = new AggTrace(parentAggTrace, trace.getMethodName()); if (trace.isSendMethod()) { aggRootTrace.setSendMethod(true); aggRootTrace.setPayload(trace.getPayload()); aggRootTrace.setOverhead(trace.getOverhead()); } int i = 0; while (i < trace.getSubTraces().size() - 1) { int currentTraceHash = trace.getSubTraces().get(i).hashCode(); int candidateSequenceStartIx = i; int candidateSequenceEndIx = i; int j = i + 1; int nextTraceHash = trace.getSubTraces().get(j).hashCode(); while (currentTraceHash != nextTraceHash && j < trace.getSubTraces().size() - 1) { j++; nextTraceHash = trace.getSubTraces().get(j).hashCode(); } AggTrace loopTrace = null; if (currentTraceHash == nextTraceHash && (j - 1) + (j - i) < trace.getSubTraces().size()) { candidateSequenceEndIx = j - 1; int sequenceLength = j - i; int sequenceHash = calculateSequenceHash(trace.getSubTraces(), candidateSequenceStartIx, candidateSequenceEndIx); int nextSequenceStart = candidateSequenceEndIx + 1; int nextSequenceEnd = candidateSequenceEndIx + sequenceLength; int nextSequenceHash = 0; int loopCount = 1; sequenceLoop: while (nextSequenceEnd < trace.getSubTraces().size()) { nextSequenceHash = calculateSequenceHash(trace.getSubTraces(), nextSequenceStart, nextSequenceEnd); if (nextSequenceHash == sequenceHash) { if (loopTrace == null) { loopTrace = new AggTrace(aggRootTrace, LOOP_STR); loopTrace.setLoop(true); for (int ix = candidateSequenceStartIx; ix <= candidateSequenceEndIx; ix++) { fromTrace(trace.getSubTraces().get(ix), loopTrace); } } loopCount++; loopTrace.setLoopCount(loopCount); i = nextSequenceEnd; nextSequenceStart += sequenceLength; nextSequenceEnd += sequenceLength; } else { break sequenceLoop; } } } if (loopTrace == null) { fromTrace(trace.getSubTraces().get(i), aggRootTrace); } i++; } if (i < trace.getSubTraces().size()) { fromTrace(trace.getSubTraces().get(i), aggRootTrace); } return aggRootTrace; } private static int calculateSequenceHash(List<Trace> childList, int candidateSequenceStartIx, int candidateSequenceEndIx) { int sequenceHash = 0; int counter = 1; for (int x = candidateSequenceStartIx; x <= candidateSequenceEndIx; x++) { sequenceHash += counter * childList.get(x).hashCode(); counter++; } return sequenceHash; } /** * @return the sendMethod */ public boolean isSendMethod() { return sendMethod; } /** * @param sendMethod * the sendMethod to set */ public void setSendMethod(boolean sendMethod) { this.sendMethod = sendMethod; } /** * @return the overhead */ public long getOverhead() { return overhead; } /** * @param overhead * the overhead to set */ public void setOverhead(long overhead) { this.overhead = overhead; } /** * @return the payload */ public long getPayload() { return payload; } /** * @param payload * the payload to set */ public void setPayload(long payload) { this.payload = payload; } }