@Test public void isReturningTheProperValue() { long methodIdent = 108L; InvocationSequenceData root = new InvocationSequenceData(DEF_DATE, 10, 10, methodIdent); InvocationSequenceData firstInvocationSequenceData = new InvocationSequenceData(DEF_DATE, 10, 10, methodIdent); firstInvocationSequenceData.setParentSequence(root); InvocationSequenceData secondInvocationSequenceData = new InvocationSequenceData(DEF_DATE, 10, 10, methodIdent); secondInvocationSequenceData.setParentSequence(root); root.getNestedSequences().add(firstInvocationSequenceData); root.getNestedSequences().add(secondInvocationSequenceData); CauseCluster one = new CauseCluster(firstInvocationSequenceData); CauseCluster two = new CauseCluster(secondInvocationSequenceData); one.setDistanceToNextCluster(1); one.setDepthOfCommonContext(1); two.setDistanceToNextCluster(1); two.setDepthOfCommonContext(1); List<CauseCluster> clustersToMerge = new ArrayList<CauseCluster>(); clustersToMerge.add(one); clustersToMerge.add(two); CauseCluster merged = new CauseCluster(clustersToMerge); assertThat("The returned problemContext must have size 2", merged.getCauseInvocations().size(), is(2)); assertThat("The returned problemContext must be the root one element", merged.getCommonContext(), is(root)); }
/** * Creates a new cluster from the passed list of {@link #CauseCluster}. Sets the * <code>commonContext</code> to the parent of the new cluster. The parent subsumes all * {@link InvocationSequenceData} the cluster currently holds. * * @param clustersToMerge * List with clusters this cluster is merged with. * */ public CauseCluster(List<CauseCluster> clustersToMerge) { if ((clustersToMerge == null) || (clustersToMerge.isEmpty())) { throw new IllegalArgumentException("Clusters are not allowed to be null or empty!"); } int distanceToParent = clustersToMerge.get(0).getDistanceToNextCluster(); InvocationSequenceData parent = clustersToMerge.get(0).getCommonContext(); for (int i = 0; (i < distanceToParent) && (parent.getParentSequence() != null); i++) { parent = parent.getParentSequence(); } commonContext = parent; for (CauseCluster cluster : clustersToMerge) { causeInvocations.addAll(cluster.getCauseInvocations()); } }
/** * In case there is just one cause invocation then the parent of the problem context is the * cause invocation, otherwise a new instance of CauseCluster will be created with the only * invocation in the list. * * @param causeInvocation * Invocation to get the parent of it if is the only one itself it has no parent. * @return Returns the CauseCluster. */ private CauseCluster getCauseCluster(InvocationSequenceData causeInvocation) { if ((causeInvocation.getParentSequence() != null) && !(causeInvocation.equals(globalContext))) { CauseCluster causeCluster = new CauseCluster(causeInvocation.getParentSequence()); causeCluster.getCauseInvocations().clear(); causeCluster.getCauseInvocations().add(causeInvocation); return causeCluster; } else { return new CauseCluster(causeInvocation); } }
for (CauseCluster cluster : causeClusters) { clustersToMerge.add(cluster); if (cluster.getDistanceToNextCluster() > distance) { if (clustersToMerge.size() > 1) { newClusters.add(new CauseCluster(clustersToMerge)); merged = true; } else {
@Test public void rootCauseMustBeNotNullWhenMethodIdentIsEqualAndTheInvocationHasTimerData() { double highDuration = RANDOM.nextDouble() + 1000; List<InvocationSequenceData> nestedSequences = new ArrayList<InvocationSequenceData>(); InvocationSequenceData firstChildSequenceData = new InvocationSequenceData(DEF_DATE, PLATFORM_IDENT, SENSOR_TYPE_IDENT, METHOD_IDENT_EQUAL); TimerData timerData = new TimerData(new Timestamp(System.currentTimeMillis()), 10L, 20L, 30L); timerData.calculateExclusiveMin(RANDOM.nextDouble()); timerData.setExclusiveDuration(RANDOM.nextDouble()); firstChildSequenceData.setTimerData(timerData); InvocationSequenceData secondChildSequenceData = new InvocationSequenceData(DEF_DATE, PLATFORM_IDENT, SENSOR_TYPE_IDENT, METHOD_IDENT_EQUAL); timerData.calculateExclusiveMin(RANDOM.nextDouble()); timerData.setExclusiveDuration(RANDOM.nextDouble()); secondChildSequenceData.setTimerData(timerData); nestedSequences.add(firstChildSequenceData); nestedSequences.add(secondChildSequenceData); when(problemContext.getCommonContext()).thenReturn(commonContext); when(commonContext.getMethodIdent()).thenReturn(1L); when(commonContext.getDuration()).thenReturn(highDuration); when(problemContext.getCauseInvocations()).thenReturn(nestedSequences); AggregatedDiagnosisData rootCause = problemCauseRule.action(); assertThat("The returned root cause rule must not be null", rootCause, notNullValue()); }
/** * Calculates the absolute maximum depth for the common context of the * <code>ProblemContext</code>. * * @return Returns the maximum depth of the recursion. */ private int calculateMaxRecursionDepth() { // The Root Causes can only be in the invocation tree with the Problem Context as root node. InvocationSequenceDataIterator iterator = new InvocationSequenceDataIterator(problemContext.getCommonContext()); // Checks if a Root Cause method is called by another Root Cause method. If so, there is // recursion. Stack<Integer> recursionStack = new Stack<>(); int maxRecursionDepth = 0; int maxIterationsToCheck = 0; while (iterator.hasNext() && (maxIterationsToCheck < MAX_CALLS_TO_CHECK) && (maxRecursionDepth < 2)) { InvocationSequenceData invocation = iterator.next(); if (!recursionStack.isEmpty() && (recursionStack.peek() >= iterator.currentDepth())) { recursionStack.pop(); } if (isCauseInvocation(invocation)) { recursionStack.push(iterator.currentDepth()); if (recursionStack.size() > maxRecursionDepth) { maxRecursionDepth = recursionStack.size(); } maxIterationsToCheck++; } } return maxRecursionDepth; }
invocation = iterator.next(); int minDepth = Math.min(iterator.currentDepth(), MAX_CLUSTER_CONTEXT_DISTANCE); if (nextCluster.getCommonContext() == invocation) { if (null != currentCluster) { int depthDistance = Math.max((currentCauseDepth - minDepth) + 1, 0); currentCluster.setDistanceToNextCluster(depthDistance); currentCluster.setDistanceToNextCluster(MAX_CLUSTER_CONTEXT_DISTANCE);
/** * Creates list with clusters. For each InvocationSequenceData in the Time Wasting Operation a * cluster is build initially. * * @return Returns a list of causeCluster. */ private List<CauseCluster> populateCauseCluster() { List<InvocationSequenceData> causeInvocations = timeWastingOperation.getRawInvocationsSequenceElements(); List<CauseCluster> causeClusters = new LinkedList<>(); for (InvocationSequenceData invocation : causeInvocations) { causeClusters.add(new CauseCluster(invocation)); } return causeClusters; }
for (CauseCluster cluster : causeClusters) { double exclusiveDurationSum = 0.0; for (InvocationSequenceData invocation : cluster.getCauseInvocations()) { exclusiveDurationSum += DiagnosisHelper.getExclusiveDuration(invocation);
@Test public void rootCauseMustHaveTwoElementsInRawInvocationSequence() { List<InvocationSequenceData> nestedSequences = new ArrayList<InvocationSequenceData>(); InvocationSequenceData firstChildSequenceData = new InvocationSequenceData(DEF_DATE, PLATFORM_IDENT, SENSOR_TYPE_IDENT, METHOD_IDENT_EQUAL); TimerData timerData = new TimerData(new Timestamp(System.currentTimeMillis()), 10L, 20L, 30L); TimerData firstTimerData = timerData; firstTimerData.setExclusiveDuration(2000); firstTimerData.setDuration(2000); firstChildSequenceData.setTimerData(firstTimerData); InvocationSequenceData secondChildSequenceData = new InvocationSequenceData(DEF_DATE, PLATFORM_IDENT, SENSOR_TYPE_IDENT, METHOD_IDENT_EQUAL); TimerData secondTimerData = timerData; secondTimerData.setExclusiveDuration(100); secondTimerData.setDuration(100); secondChildSequenceData.setTimerData(secondTimerData); nestedSequences.add(firstChildSequenceData); nestedSequences.add(secondChildSequenceData); when(problemContext.getCommonContext()).thenReturn(commonContext); when(commonContext.getMethodIdent()).thenReturn(METHOD_IDENT_EQUAL); when(commonContext.getDuration()).thenReturn(3100.0); when(problemContext.getCauseInvocations()).thenReturn(nestedSequences); AggregatedDiagnosisData rootCause = problemCauseRule.action(); assertThat("Raw invocation sequence must have two elements", rootCause.getRawInvocationsSequenceElements(), hasSize(2)); }
/** * Aggregate all the invocations that are considered as rootCause to the object * {@link RootCause}. * * @return Returns the number of candidates that are already checked. */ private int getRootCauses() { int causeCandidatesChecked = 0; double problemContextCommonContextDuration = InvocationSequenceDataHelper.calculateDuration(problemContext.getCommonContext()); // Root Cause candidates are put into one Root Cause as long as the condition is true. while ((sumExclusiveTime < (PROPORTION * problemContextCommonContextDuration)) && (causeCandidatesChecked < causeCandidates.size())) { InvocationSequenceData invocation = causeCandidates.get(causeCandidatesChecked); if (null == rootCause) { rootCause = diagnosisDataAggregator.getAggregatedDiagnosisData(invocation); } else { diagnosisDataAggregator.aggregate(rootCause, invocation); } sumExclusiveTime += DiagnosisHelper.getExclusiveDuration(invocation); causeCandidatesChecked++; } return causeCandidatesChecked; }
@Test(expectedExceptions = RuntimeException.class) public void collectProblemInstancesWithRuntimeExceptionCauseStructure() { AggregatedDiagnosisData aggregatedInvocationSequenceData = null; aggregatedInvocationSequenceData = DiagnosisDataAggregator.getInstance().getAggregatedDiagnosisData(secondChildSequence); DiagnosisDataAggregator.getInstance().aggregate(aggregatedInvocationSequenceData, secondChildSequence); Multimap<String, Tag> tagMap = ArrayListMultimap.create(); Tag tagOne = new Tag(RuleConstants.DIAGNOSIS_TAG_GLOBAL_CONTEXT, secondChildSequence, Tags.rootTag(secondChildSequence)); Tag tagTwo = new Tag(RuleConstants.DIAGNOSIS_TAG_PROBLEM_CONTEXT, new CauseCluster(secondChildSequence), tagOne); Tag tagThree = new Tag(RuleConstants.DIAGNOSIS_TAG_PROBLEM_CAUSE, aggregatedInvocationSequenceData, tagTwo); Tag tagFour = new Tag(RuleConstants.DIAGNOSIS_TAG_CAUSE_STRUCTURE, "Test", tagThree); tagMap.put("D", tagFour); when(sessionContext.getInput()).thenReturn(secondChildSequence); when(sessionContext.getStorage()).thenReturn(storage); when(storage.mapTags(TagState.LEAF)).thenReturn(tagMap); ProblemOccurrenceResultCollector problemInstanceResultCollector = new ProblemOccurrenceResultCollector(); List<ProblemOccurrence> problemOccurrence = problemInstanceResultCollector.collect(sessionContext); assertThat(problemOccurrence, hasSize(0)); }
/** * Rule execution. * * @return DIAGNOSIS_TAG_PROBLEM_CAUSE */ @Action(resultTag = RuleConstants.DIAGNOSIS_TAG_PROBLEM_CAUSE) public AggregatedDiagnosisData action() { causeCandidates = problemContext.getCauseInvocations(); int causeCandidatesChecked = getRootCauses(); threeSigmaApproach(causeCandidatesChecked); return rootCause; }
@Test public void wrongGlobalContextResultsInWrongCommonContext() { long methodIdent = 108L; InvocationSequenceData root = new InvocationSequenceData(DEF_DATE, 10, 10, methodIdent); InvocationSequenceData firstInvocationSequenceData = new InvocationSequenceData(DEF_DATE, 10, 10, methodIdent); firstInvocationSequenceData.setParentSequence(root); InvocationSequenceData secondInvocationSequenceData = new InvocationSequenceData(DEF_DATE, 10, 10, methodIdent); secondInvocationSequenceData.setParentSequence(root); root.getNestedSequences().add(firstInvocationSequenceData); root.getNestedSequences().add(secondInvocationSequenceData); CauseCluster one = new CauseCluster(root); CauseCluster two = new CauseCluster(secondInvocationSequenceData); one.setDistanceToNextCluster(1); one.setDepthOfCommonContext(1); two.setDistanceToNextCluster(1); two.setDepthOfCommonContext(1); List<CauseCluster> clustersToMerge = new ArrayList<CauseCluster>(); clustersToMerge.add(one); clustersToMerge.add(two); CauseCluster merged = new CauseCluster(clustersToMerge); assertThat("The returned problemContext must has size 2", merged.getCauseInvocations().size(), is(2)); assertThat("The returned root cause rule must not be null", merged.getCommonContext(), not(nullValue())); }
Pair<Long, String> pair = new Pair<Long, String>(detectedProblemContext.getNestedSequences().get(0).getMethodIdent(), "n.a."); when(rootCause.getAggregationKey()).thenReturn(pair); when(problemContext.getCommonContext()).thenReturn(commonContext); when(commonContext.getParentSequence()).thenReturn(null); when(commonContext.getNestedSequences()).thenReturn(Collections.singletonList(detectedProblemContext)); when(commonContext.getMethodIdent()).thenReturn(firstMethod.getMethodIdent()); when(commonContext.getTimerData()).thenReturn(firstMethod.getTimerData()); when(problemContext.getCauseInvocations()).thenReturn(detectedProblemContext.getNestedSequences()); when(commonContext.getDuration()).thenReturn(firstMethod.getDuration());
@Test public void problemContextMustBeGlobalContext() { Timestamp currentTime = new Timestamp(System.currentTimeMillis()); List<InvocationSequenceData> rawInvocations = new ArrayList<InvocationSequenceData>(); TimerData timerData = new TimerData(currentTime, 10L, 20L, 30L); timerData.calculateExclusiveMin(RANDOM.nextDouble()); timerData.setExclusiveDuration(RANDOM.nextDouble()); when(globalContext.getTimerData()).thenReturn(timerData); rawInvocations.add(globalContext); when(timeWastingOperation.getRawInvocationsSequenceElements()).thenReturn(rawInvocations); CauseCluster problemContext = problemContextRule.action(); assertThat("The returned problemContext must be the most significant cluster context", problemContext.getCommonContext(), is(globalContext)); }
@Test public void collectProblemInstances() { CauseStructure causeStructure = new CauseStructure(CauseType.SINGLE, SourceType.TIMERDATA); AggregatedDiagnosisData aggregatedInvocationSequenceData = DiagnosisDataAggregator.getInstance().getAggregatedDiagnosisData(secondChildSequence); DiagnosisDataAggregator.getInstance().aggregate(aggregatedInvocationSequenceData, secondChildSequence); Multimap<String, Tag> tagMap = ArrayListMultimap.create(); Tag tagOne = new Tag(RuleConstants.DIAGNOSIS_TAG_GLOBAL_CONTEXT, secondChildSequence, Tags.rootTag(secondChildSequence)); Tag tagTwo = new Tag(RuleConstants.DIAGNOSIS_TAG_PROBLEM_CONTEXT, new CauseCluster(secondChildSequence), tagOne); Tag tagThree = new Tag(RuleConstants.DIAGNOSIS_TAG_PROBLEM_CAUSE, aggregatedInvocationSequenceData, tagTwo); Tag tagFour = new Tag(RuleConstants.DIAGNOSIS_TAG_CAUSE_STRUCTURE, causeStructure, tagThree); tagMap.put("Test", tagFour); when(sessionContext.getInput()).thenReturn(secondChildSequence); when(sessionContext.getStorage()).thenReturn(storage); when(storage.mapTags(TagState.LEAF)).thenReturn(tagMap); ProblemOccurrenceResultCollector problemInstanceResultCollector = new ProblemOccurrenceResultCollector(); List<ProblemOccurrence> problemOccurrence = problemInstanceResultCollector.collect(sessionContext); assertThat(problemOccurrence, hasSize(1)); }
when(rootCause.getAggregatedDiagnosisTimerData()).thenReturn(aggregatedTimerData); when(rootCause.getAggregationKey()).thenReturn(detectedProblemContext.getNestedSequences().get(0).getMethodIdent()); when(problemContext.getCommonContext()).thenReturn(commonContext); when(commonContext.getParentSequence()).thenReturn(null); when(commonContext.getNestedSequences()).thenReturn(Collections.singletonList(detectedProblemContext)); when(commonContext.getMethodIdent()).thenReturn(firstMethod.getMethodIdent()); when(commonContext.getTimerData()).thenReturn(firstMethod.getTimerData()); when(problemContext.getCauseInvocations()).thenReturn(detectedProblemContext.getNestedSequences()); when(commonContext.getDuration()).thenReturn(firstMethod.getDuration());
@Test public void problemContextMustBeTheSameInvocationIfItIsTheOnlyOneAndIsTheInvoker() { long methodIdent = 108L; Timestamp defDate = new Timestamp(new Date().getTime()); long platformIdent = RANDOM.nextLong(); long sensorTypeIdent = RANDOM.nextLong(); InvocationSequenceData parentSequence = new InvocationSequenceData(defDate, platformIdent, sensorTypeIdent, methodIdent); InvocationSequenceData childSequence = new InvocationSequenceData(defDate, platformIdent, sensorTypeIdent, methodIdent); List<InvocationSequenceData> rawInvocations = new ArrayList<InvocationSequenceData>(); parentSequence.getNestedSequences().add(childSequence); rawInvocations.add(parentSequence); when(timeWastingOperation.getRawInvocationsSequenceElements()).thenReturn(rawInvocations); CauseCluster problemContext = problemContextRule.action(); assertThat("The returned problemContext must be the invoker", problemContext.getCommonContext(), is(parentSequence)); }
@Test(expectedExceptions = RuntimeException.class) public void collectProblemInstancesWithRuntimeExceptionGlobalContext() { CauseStructure causeStructure = new CauseStructure(CauseType.SINGLE, SourceType.TIMERDATA); AggregatedDiagnosisData aggregatedInvocationSequenceData = null; aggregatedInvocationSequenceData = DiagnosisDataAggregator.getInstance().getAggregatedDiagnosisData(secondChildSequence); DiagnosisDataAggregator.getInstance().aggregate(aggregatedInvocationSequenceData, secondChildSequence); Multimap<String, Tag> tagMap = ArrayListMultimap.create(); Tag tagOne = new Tag(RuleConstants.DIAGNOSIS_TAG_GLOBAL_CONTEXT, "Test", Tags.rootTag(secondChildSequence)); Tag tagTwo = new Tag(RuleConstants.DIAGNOSIS_TAG_PROBLEM_CONTEXT, new CauseCluster(secondChildSequence), tagOne); Tag tagThree = new Tag(RuleConstants.DIAGNOSIS_TAG_PROBLEM_CAUSE, aggregatedInvocationSequenceData, tagTwo); Tag tagFour = new Tag(RuleConstants.DIAGNOSIS_TAG_CAUSE_STRUCTURE, causeStructure, tagThree); tagMap.put("D", tagFour); when(sessionContext.getInput()).thenReturn(secondChildSequence); when(sessionContext.getStorage()).thenReturn(storage); when(storage.mapTags(TagState.LEAF)).thenReturn(tagMap); ProblemOccurrenceResultCollector problemInstanceResultCollector = new ProblemOccurrenceResultCollector(); List<ProblemOccurrence> problemOccurrence = problemInstanceResultCollector.collect(sessionContext); assertThat(problemOccurrence, hasSize(0)); }