in src/core/org.openjdk.jmc.flightrecorder.rules.jdk/src/main/java/org/openjdk/jmc/flightrecorder/rules/jdk/latency/MethodProfilingRule.java [378:543]
private IUnorderedWindowVisitor createWindowVisitor(
final PeriodRangeMap settings, final IItemFilter settingsFilter, final IQuantity windowSize,
final List<MethodProfilingWindowResult> rawScores, final FutureTask<IResult> evaluationTask,
final Pattern excludes) {
return new IUnorderedWindowVisitor() {
@Override
public void visitWindow(IItemCollection items, IQuantity startTime, IQuantity endTime) {
IRange<IQuantity> windowRange = QuantityRange.createWithEnd(startTime, endTime);
if (RulesToolkit.getSettingMaxPeriod(items, JdkTypeIDs.EXECUTION_SAMPLE) == null) {
Pair<Pair<IQuantity, IQuantity>, IMCStackTrace> resultPair = performCalculation(items,
settings.getSetting(startTime));
if (resultPair != null) {
rawScores.add(new MethodProfilingWindowResult(resultPair.right.getFrames().get(0).getMethod(),
resultPair.right, resultPair.left.left, resultPair.left.right, windowRange));
}
} else {
Set<IQuantity> settingTimes = items.apply(settingsFilter).getAggregate(
(IAggregator<Set<IQuantity>, ?>) Aggregators.distinct(JfrAttributes.START_TIME));
IQuantity start = startTime;
List<Pair<Pair<IQuantity, IQuantity>, IMCStackTrace>> scores = new ArrayList<>(settingTimes.size());
for (IQuantity settingTime : settingTimes) {
IItemFilter window = ItemFilters.interval(JfrAttributes.END_TIME, start, true, settingTime,
true);
scores.add(performCalculation(items.apply(window), settings.getSetting(start)));
start = settingTime;
}
Map<IMCStackTrace, Pair<IQuantity, IQuantity>> scoresByMethod = new HashMap<>();
for (Pair<Pair<IQuantity, IQuantity>, IMCStackTrace> score : scores) {
if (score != null) {
if (scoresByMethod.get(score.right) == null) {
scoresByMethod.put(score.right, score.left);
} else {
scoresByMethod.put(score.right,
new Pair<>(score.left.left.add(scoresByMethod.get(score.right).left),
score.left.right.add(scoresByMethod.get(score.right).right)));
}
}
}
IQuantity sumScore = UnitLookup.PERCENT_UNITY.quantity(0);
IQuantity actualScore = UnitLookup.PERCENT_UNITY.quantity(0);
IMCStackTrace hottestPath = null;
for (Entry<IMCStackTrace, Pair<IQuantity, IQuantity>> entry : scoresByMethod.entrySet()) {
if (entry.getValue().left.compareTo(sumScore) > 0) {
hottestPath = entry.getKey();
actualScore = entry.getValue().right;
sumScore = sumScore.add(entry.getValue().left);
}
}
IQuantity averageOfAllPossibleSamples = sumScore.multiply(1d / scores.size());
IMCMethod hottestMethod = (hottestPath == null ? null : hottestPath.getFrames().get(0).getMethod());
rawScores.add(new MethodProfilingWindowResult(hottestMethod, hottestPath,
averageOfAllPossibleSamples, actualScore, windowRange));
}
}
@Override
public boolean shouldContinue() {
return evaluationTask != null && !evaluationTask.isCancelled();
}
/**
* Performs the actual calculation of the score for the given period of the recording.
*
* @param items
* the items to base the score on
* @param period
* the periodicity to base the relevancy calculation on
* @return a double value in the interval [0,1] with 1 being a system in completely
* saturated load with only one method called
*/
private Pair<Pair<IQuantity, IQuantity>, IMCStackTrace> performCalculation(
IItemCollection items, IQuantity period) {
IItemCollection filteredItems = items.apply(JdkFilters.EXECUTION_SAMPLE);
final IMCMethod[] maxMethod = new IMCMethod[1];
final IMCStackTrace[] maxPath = new IMCStackTrace[1];
// Using this GroupingAggregator because it's the only way to extract the keys from the aggregation along with values
IAggregator<IQuantity, ?> aggregator = GroupingAggregator.build("", "", //$NON-NLS-1$ //$NON-NLS-2$
MethodProfilingDataProvider.PATH_ACCESSOR_FACTORY, Aggregators.count(),
new GroupingAggregator.IGroupsFinisher<IQuantity, IMCStackTrace, CountConsumer>() {
@Override
public IType<IQuantity> getValueType() {
return UnitLookup.NUMBER;
}
@Override
public IQuantity getValue(
Iterable<? extends GroupEntry<IMCStackTrace, CountConsumer>> groupEntries) {
HashMap<IMCMethod, IQuantity> map = new HashMap<>();
HashMap<IMCMethod, IMCStackTrace> pathMap = new HashMap<>();
int total = 0;
// When we group by stack trace we can run into situations where the top frames are otherwise the same
// for our purposes (finding the hottest method), but they differ by BCI, throwing off the count.
// so we should collect further on the method for the top frame.
for (GroupEntry<IMCStackTrace, CountConsumer> group : groupEntries) {
IMCStackTrace trace = processPath(group.getKey());
total += group.getConsumer().getCount();
if (!trace.getFrames().isEmpty()) {
IMCMethod topFrameMethod = trace.getFrames().get(0).getMethod();
if (map.get(topFrameMethod) == null) {
map.put(topFrameMethod,
UnitLookup.NUMBER_UNITY.quantity(group.getConsumer().getCount()));
pathMap.put(topFrameMethod, trace);
} else {
IQuantity old = map.get(topFrameMethod);
map.put(topFrameMethod, old.add(
UnitLookup.NUMBER_UNITY.quantity(group.getConsumer().getCount())));
}
}
}
if (!pathMap.isEmpty() && !map.isEmpty()) {
Entry<IMCMethod, IQuantity> topEntry = Collections.max(map.entrySet(),
new Comparator<Entry<IMCMethod, IQuantity>>() {
@Override
public int compare(
Entry<IMCMethod, IQuantity> arg0,
Entry<IMCMethod, IQuantity> arg1) {
return arg0.getValue().compareTo(arg1.getValue());
}
});
maxPath[0] = pathMap.get(topEntry.getKey());
maxMethod[0] = topEntry.getKey();
return topEntry.getValue().multiply(1d / total);
}
return UnitLookup.NUMBER_UNITY.quantity(0);
}
private IMCStackTrace processPath(IMCStackTrace path) {
List<IMCFrame> frames = new ArrayList<>(path.getFrames());
List<IMCFrame> framesToDrop = new ArrayList<IMCFrame>();
// Drop any frames that match the excluded pattern, thereby treating the first non-matching frame that we encounter as the hot one.
for (IMCFrame frame : frames) {
IMCPackage p = frame.getMethod().getType().getPackage();
// Under some circumstances p.getName() will return a raw null, we need to handle this case.
Matcher m = excludes.matcher(p.getName() == null ? "" : p.getName()); //$NON-NLS-1$
if (m.matches()) {
framesToDrop.add(frame);
} else {
break;
}
}
frames.removeAll(framesToDrop);
return new MCStackTrace(frames, path.getTruncationState());
}
});
IQuantity maxRatio = filteredItems.getAggregate(aggregator);
Pair<Pair<IQuantity, IQuantity>, IMCStackTrace> result = null;
if (maxMethod[0] != null && maxRatio != null && period != null) { // ignoring if there are no samples or if we don't yet know the periodicity
double periodsPerSecond = 1 / period.doubleValueIn(UnitLookup.SECOND);
double maxSamplesPerSecond = SAMPLES_PER_PERIOD * periodsPerSecond;
double samplesInPeriod = items
.getAggregate(Aggregators.count(ItemFilters.type(JdkTypeIDs.EXECUTION_SAMPLE)))
.doubleValueIn(UnitLookup.NUMBER_UNITY);
double maxSamplesInPeriod = maxSamplesPerSecond * windowSize.doubleValueIn(UnitLookup.SECOND);
double relevancy = samplesInPeriod / maxSamplesInPeriod;
double highestRatioOfSamples = maxRatio.doubleValueIn(UnitLookup.NUMBER_UNITY);
IQuantity percentOfActualSamples = UnitLookup.PERCENT_UNITY.quantity(highestRatioOfSamples);
IQuantity percentOfAllPossibleSamples = UnitLookup.PERCENT_UNITY
.quantity(highestRatioOfSamples * relevancy);
result = new Pair<>(new Pair<>(percentOfAllPossibleSamples, percentOfActualSamples), maxPath[0]);
}
return result;
}
};
}