private Result getResult()

in core/org.openjdk.jmc.flightrecorder.rules.jdk/src/main/java/org/openjdk/jmc/flightrecorder/rules/jdk/memory/IncreasingLiveSetRule.java [100:236]


	private Result getResult(IItemCollection items, IPreferenceValueProvider valueProvider) {
		EventAvailability eventAvailability = RulesToolkit.getEventAvailability(items, JdkTypeIDs.HEAP_SUMMARY);
		if (eventAvailability == EventAvailability.UNKNOWN || eventAvailability == EventAvailability.DISABLED) {
			return RulesToolkit.getEventAvailabilityResult(this, items, eventAvailability, JdkTypeIDs.HEAP_SUMMARY);
		}

		IQuantity postWarmupTime = getPostWarmupTime(items, valueProvider.getPreferenceValue(CLASSES_LOADED_PERCENT));
		Iterator<? extends IItemIterable> allAfterItems = items.apply(JdkFilters.HEAP_SUMMARY_AFTER_GC).iterator();
		double score = 0;
		IQuantity liveSetIncreasePerSecond = UnitLookup.MEMORY.getUnit(BinaryPrefix.MEBI).quantity(0);
		if (allAfterItems.hasNext()) {
			// FIXME: Handle multiple IItemIterable
			IItemIterable afterItems = allAfterItems.next();
			IMemberAccessor<IQuantity, IItem> timeAccessor = JfrAttributes.END_TIME.getAccessor(afterItems.getType());
			IMemberAccessor<IQuantity, IItem> memAccessor = JdkAttributes.HEAP_USED.getAccessor(afterItems.getType());

			liveSetIncreasePerSecond = UnitLookup.MEMORY.getUnit(BinaryPrefix.MEBI)
					.quantity(RulesToolkit.leastSquareMemory(afterItems.iterator(), timeAccessor, memAccessor));

			if (postWarmupTime == null) {
				return RulesToolkit.getTooFewEventsResult(this);
			}
			IQuantity postWarmupHeapSize = items
					.apply(ItemFilters.and(JdkFilters.HEAP_SUMMARY_AFTER_GC,
							ItemFilters.moreOrEqual(JfrAttributes.START_TIME, postWarmupTime)))
					.getAggregate(JdkAggregators.first(JdkAttributes.HEAP_USED));
			if (postWarmupHeapSize == null) {
				return RulesToolkit.getTooFewEventsResult(this);
			}
			double relativeIncreasePerSecond = liveSetIncreasePerSecond.ratioTo(postWarmupHeapSize);
			score = RulesToolkit.mapExp100(relativeIncreasePerSecond, PERCENT_OF_HEAP_INCREASE_PER_SECOND);
		}
		// If we have Old Object Sample events we can attempt to find suitable memory leak class candidates
		// otherwise we just return the basic increasing live set score
		EventAvailability ea = RulesToolkit.getEventAvailability(items, JdkTypeIDs.OLD_OBJECT_SAMPLE);
		// FIXME: Should construct an message using memoryIncrease, not use a hard limit
		if (score >= 25 && (ea == EventAvailability.DISABLED || ea == EventAvailability.UNKNOWN)) {
			IQuantity timeAfterJVMStart = items.getAggregate(JdkAggregators.FIRST_ITEM_START).subtract(items.getAggregate(JdkAggregators.JVM_START_TIME));
			String shortMessage = MessageFormat.format(
					Messages.getString(Messages.IncreasingLiveSetRuleFactory_TEXT_INFO),
					liveSetIncreasePerSecond.displayUsing(IDisplayable.AUTO));
			String longMessage = shortMessage + "<p>" //$NON-NLS-1$
					+ MessageFormat.format(Messages.getString(Messages.IncreasingLiveSetRuleFactory_TEXT_INFO_LONG),
							timeAfterJVMStart.displayUsing(IDisplayable.AUTO));
			return new Result(this, score, shortMessage, longMessage, JdkQueries.HEAP_SUMMARY_AFTER_GC);
		} else if (score < 25) {
			return new Result(this, score, Messages.getString(Messages.IncreasingLiveSetRule_TEXT_OK));
		}

		// step 1. extract events from after the estimated warmup period
		IItemCollection oldObjectItems = items.apply(ItemFilters.and(ItemFilters.type(JdkTypeIDs.OLD_OBJECT_SAMPLE),
				ItemFilters.more(JfrAttributes.START_TIME, postWarmupTime)));

		ReferenceTreeModel tree = ReferenceTreeModel.buildReferenceTree(oldObjectItems);

		// step 2. perform a balance calculation on the old object sample events aggregated by class count
		boolean anyReferrerChains = false;
		for (ReferenceTreeObject referenceTreeObject : tree.getLeakObjects()) {
			if (referenceTreeObject.getParent() != null) {
				anyReferrerChains = true;
				break;
			}
		}
		if (!anyReferrerChains) {
			List<IntEntry<IMCType>> calculateGroupingScore = RulesToolkit.calculateGroupingScore(oldObjectItems,
					JdkAttributes.OLD_OBJECT_CLASS);
			double calculateBalanceScore = RulesToolkit.calculateBalanceScore(calculateGroupingScore);
			String shortDescription = MessageFormat.format(Messages.IncreasingLiveSetRuleFactory_TEXT_INFO,
					liveSetIncreasePerSecond.displayUsing(IDisplayable.AUTO))
					+ (calculateBalanceScore >= 25
							? Messages.getString(Messages.IncreasingLiveSetRule_TEXT_INFO_UNBALANCED)
							: Messages.getString(Messages.IncreasingLiveSetRule_TEXT_INFO_BALANCED));
			return new Result(this, Math.min(calculateBalanceScore, 25), // because we already know that there is a leak.
					shortDescription, Messages.getString(Messages.IncreasingLiveSetRule_TEXT_INFO_LONG));
		}

		List<ReferenceTreeObject> leakCandidates = tree.getLeakCandidates(
				valueProvider.getPreferenceValue(RELEVANCE_THRESHOLD).doubleValueIn(UnitLookup.NUMBER_UNITY));
		if (leakCandidates.size() > 0) {
			StringBuilder descriptionBuilder = new StringBuilder();
			descriptionBuilder
					.append(MessageFormat.format(Messages.getString(Messages.IncreasingLiveSetRuleFactory_TEXT_INFO),
							liveSetIncreasePerSecond.displayUsing(IDisplayable.AUTO)));
			descriptionBuilder.append("<br/>"); //$NON-NLS-1$
			descriptionBuilder.append(MessageFormat
					.format(Messages.getString(Messages.IncreasingLiveSetRule_LEAK_CANDIDATES), leakCandidates.size()));
			descriptionBuilder.append("<ul>"); //$NON-NLS-1$
			int objectFormat = ReferenceTreeObject.FORMAT_PACKAGE | ReferenceTreeObject.FORMAT_FIELD
					| ReferenceTreeObject.FORMAT_ARRAY_INFO;
			for (ReferenceTreeObject candidate : leakCandidates) {
				descriptionBuilder.append("<li>"); //$NON-NLS-1$
				descriptionBuilder.append(candidate.toString(objectFormat));
				descriptionBuilder.append("<br/>"); //$NON-NLS-1$
				descriptionBuilder.append(Messages.getString(Messages.IncreasingLiveSetRule_CANDIDATE_REFERRED_BY));
				descriptionBuilder.append("<ul>"); //$NON-NLS-1$
				ReferenceTreeObject chainObject = candidate.getParent();
				for (int i = 0; i < 10 && chainObject != null; i++) {
					descriptionBuilder.append("<li>"); //$NON-NLS-1$
					descriptionBuilder.append(chainObject.toString(objectFormat));
					if (chainObject.getParent() == null) { // aborting the loop because we have found the root
						descriptionBuilder.append(" ("); //$NON-NLS-1$
						descriptionBuilder.append(chainObject.getRootDescription());
						descriptionBuilder.append(")</li>"); //$NON-NLS-1$
						break;
					}
					descriptionBuilder.append("</li>"); //$NON-NLS-1$
					chainObject = chainObject.getParent();
				}
				if (chainObject != null && chainObject.getParent() != null) { // we never iterated to the object
					while (chainObject.getParent() != null) {
						chainObject = chainObject.getParent();
					}
					descriptionBuilder.append("<li>"); //$NON-NLS-1$
					descriptionBuilder.append(Messages.getString(Messages.IncreasingLiveSetRule_ELLIPSIS));
					descriptionBuilder.append("</li><li>"); //$NON-NLS-1$
					descriptionBuilder.append(chainObject.toString(objectFormat));
					descriptionBuilder.append(" ("); //$NON-NLS-1$
					descriptionBuilder.append(chainObject.getRootDescription());
					descriptionBuilder.append(")</li>"); //$NON-NLS-1$
				}
				descriptionBuilder.append("</ul>"); //$NON-NLS-1$
				descriptionBuilder.append("</li>"); //$NON-NLS-1$
			}
			descriptionBuilder.append("</ul>"); //$NON-NLS-1$
			return new Result(this, score, descriptionBuilder.toString());
		}
		String description = ""; //$NON-NLS-1$
		if (score >= 25) {
			description = MessageFormat.format(Messages.getString(Messages.IncreasingLiveSetRuleFactory_TEXT_INFO),
					liveSetIncreasePerSecond.displayUsing(IDisplayable.AUTO)) + "</br>"; //$NON-NLS-1$
		}
		return new Result(this, score,
				description + MessageFormat.format(
						Messages.getString(Messages.IncreasingLiveSetRule_TEXT_INFO_NO_CANDIDATES),
						postWarmupTime.displayUsing(IDisplayable.AUTO)),
				null, JdkQueries.HEAP_SUMMARY_AFTER_GC);
	}