--- old/core/org.openjdk.jmc.flightrecorder.rules.jdk/src/main/java/org/openjdk/jmc/flightrecorder/rules/jdk/latency/MethodProfilingRule.java 2018-11-29 16:38:46.244973832 -0500 +++ new/core/org.openjdk.jmc.flightrecorder.rules.jdk/src/main/java/org/openjdk/jmc/flightrecorder/rules/jdk/latency/MethodProfilingRule.java 2018-11-29 16:38:46.175974062 -0500 @@ -47,8 +47,11 @@ import java.util.concurrent.Callable; import java.util.concurrent.FutureTask; import java.util.concurrent.RunnableFuture; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.openjdk.jmc.common.IDisplayable; +import org.openjdk.jmc.common.IMCFrame; import org.openjdk.jmc.common.IMCMethod; import org.openjdk.jmc.common.IMCStackTrace; import org.openjdk.jmc.common.item.Aggregators; @@ -70,6 +73,7 @@ import org.openjdk.jmc.common.unit.UnitLookup; import org.openjdk.jmc.common.util.FormatToolkit; import org.openjdk.jmc.common.util.IPreferenceValueProvider; +import org.openjdk.jmc.common.util.MCStackTrace; import org.openjdk.jmc.common.util.Pair; import org.openjdk.jmc.common.util.TypedPreference; import org.openjdk.jmc.flightrecorder.JfrAttributes; @@ -85,7 +89,6 @@ import org.openjdk.jmc.flightrecorder.rules.util.RulesToolkit.EventAvailability; import org.openjdk.jmc.flightrecorder.rules.util.SlidingWindowToolkit; import org.openjdk.jmc.flightrecorder.rules.util.SlidingWindowToolkit.IUnorderedWindowVisitor; -import org.openjdk.jmc.flightrecorder.stacktrace.StacktraceModel; /** * Rule that calculates the top method balance in a sliding window throughout the recording with a @@ -168,7 +171,12 @@ Messages.getString(Messages.MethodProfilingRule_WINDOW_SIZE), Messages.getString(Messages.MethodProfilingRule_WINDOW_SIZE_DESC), UnitLookup.TIMESPAN, UnitLookup.SECOND.quantity(30)); - private static final List> CONFIG_ATTRIBUTES = Arrays.> asList(WINDOW_SIZE); + public static final TypedPreference EXCLUDED_PACKAGE_REGEXP = new TypedPreference<>( + "method.profiling.evaluation.excluded.package", //$NON-NLS-1$ + Messages.getString(Messages.MethodProfilingRule_EXCLUDED_PACKAGES), + Messages.getString(Messages.MethodProfilingRule_EXCLUDED_PACKAGES_DESC), + UnitLookup.PLAIN_TEXT.getPersister(), ""); + private static final List> CONFIG_ATTRIBUTES = Arrays.> asList(WINDOW_SIZE, EXCLUDED_PACKAGE_REGEXP); /** * Private Callable implementation specifically used to avoid storing the FutureTask as a field. @@ -217,10 +225,17 @@ IQuantity windowSize = valueProvider.getPreferenceValue(WINDOW_SIZE); IQuantity slideSize = UnitLookup.SECOND.quantity(windowSize.ratioTo(UnitLookup.SECOND.quantity(2))); - + String excludedPattern = valueProvider.getPreferenceValue(EXCLUDED_PACKAGE_REGEXP); + Pattern excludes; + try { + excludes = Pattern.compile(excludedPattern); + } catch (Exception e) { + // Make sure we don't blow up on an invalid pattern. + excludes = Pattern.compile(""); + } List windowResults = new ArrayList<>(); IUnorderedWindowVisitor visitor = createWindowVisitor(settings, settingsFilter, windowSize, windowResults, - evaluationTask); + evaluationTask, excludes); SlidingWindowToolkit.slidingWindowUnordered(visitor, items, windowSize, slideSize); // If a window visitor over a non empty quantity of events is guaranteed to always generate at minimum one raw score, this can be removed. if (windowResults.isEmpty()) { @@ -320,7 +335,7 @@ */ private IUnorderedWindowVisitor createWindowVisitor( final PeriodRangeMap settings, final IItemFilter settingsFilter, final IQuantity windowSize, - final List rawScores, final FutureTask evaluationTask) { + final List rawScores, final FutureTask evaluationTask, final Pattern excludes) { return new IUnorderedWindowVisitor() { @Override public void visitWindow(IItemCollection items, IQuantity startTime, IQuantity endTime) { @@ -403,14 +418,17 @@ // 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 group : groupEntries) { + IMCStackTrace trace = processPath(group.getKey()); total += group.getConsumer().getCount(); - IMCMethod topFrameMethod = group.getKey().getFrames().get(0).getMethod(); - if (map.get(topFrameMethod) == null) { - map.put(topFrameMethod, UnitLookup.NUMBER_UNITY.quantity(group.getConsumer().getCount())); - pathMap.put(topFrameMethod, group.getKey()); - } else { - IQuantity old = map.get(topFrameMethod); - map.put(topFrameMethod, old.add(UnitLookup.NUMBER_UNITY.quantity(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()) { @@ -427,6 +445,23 @@ } return UnitLookup.NUMBER_UNITY.quantity(0); } + + private IMCStackTrace processPath(IMCStackTrace path) { + List frames = (List) path.getFrames(); + List newFrames = new ArrayList(); + List framesToDrop = new ArrayList(); + // 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) { + Matcher m = excludes.matcher(FormatToolkit.getHumanReadable(frame.getMethod(), false, false, true, true, false, false)); + if (m.matches()) { + framesToDrop.add(frame); + } else { + break; + } + } + frames.removeAll(framesToDrop); + return new MCStackTrace(frames, path.getTruncationState()); + } }); IQuantity maxRatio = filteredItems.getAggregate(aggregator); --- old/core/org.openjdk.jmc.flightrecorder.rules.jdk/src/main/java/org/openjdk/jmc/flightrecorder/rules/jdk/messages/internal/Messages.java 2018-11-29 16:38:46.522972902 -0500 +++ new/core/org.openjdk.jmc.flightrecorder.rules.jdk/src/main/java/org/openjdk/jmc/flightrecorder/rules/jdk/messages/internal/Messages.java 2018-11-29 16:38:46.453973133 -0500 @@ -396,6 +396,8 @@ public static final String MethodProfilingRule_RULE_NAME = "MethodProfilingRule_RULE_NAME"; //$NON-NLS-1$ public static final String MethodProfilingRule_WINDOW_SIZE = "MethodProfilingRule_WINDOW_SIZE"; //$NON-NLS-1$ public static final String MethodProfilingRule_WINDOW_SIZE_DESC = "MethodProfilingRule_WINDOW_SIZE_DESC"; //$NON-NLS-1$ + public static final String MethodProfilingRule_EXCLUDED_PACKAGES = "MethodProfilingRule_EXCLUDED_PACKAGES"; //$NON-NLS-1$ + public static final String MethodProfilingRule_EXCLUDED_PACKAGES_DESC = "MethodProfilingRule_EXCLUDED_PACKAGES_DESC"; //$NON-NLS-1$ public static final String NumberOfGcThreadsRuleFactory_TEXT_INFO = "NumberOfGcThreadsRuleFactory_TEXT_INFO"; //$NON-NLS-1$ public static final String NumberOfGcThreadsRuleFactory_TEXT_INFO_LONG = "NumberOfGcThreadsRuleFactory_TEXT_INFO_LONG"; //$NON-NLS-1$ public static final String ObjectStatisticsDataProvider_AGGR_LIVE_SIZE_INCREASE = "ObjectStatisticsDataProvider_AGGR_LIVE_SIZE_INCREASE"; //$NON-NLS-1$ --- old/core/org.openjdk.jmc.flightrecorder.rules.jdk/src/main/resources/org/openjdk/jmc/flightrecorder/rules/jdk/messages/internal/messages.properties 2018-11-29 16:38:46.805971956 -0500 +++ new/core/org.openjdk.jmc.flightrecorder.rules.jdk/src/main/resources/org/openjdk/jmc/flightrecorder/rules/jdk/messages/internal/messages.properties 2018-11-29 16:38:46.733972197 -0500 @@ -437,6 +437,8 @@ MethodProfilingRule_RULE_NAME=Method Profiling MethodProfilingRule_WINDOW_SIZE=Method profiling window size MethodProfilingRule_WINDOW_SIZE_DESC=The size of the sliding window to use for evaluating the method profiling samples in this recording. If the evaluation of this rule takes a long time, consider increasing this parameter. Note an increased window size may reduce the accuracy of the rule. +MethodProfilingRule_EXCLUDED_PACKAGES=Packages to exclude from the stack trace +MethodProfilingRule_EXCLUDED_PACKAGES_DESC=The packages to exclude when traversing stack traces. Drop all frames matching the pattern until reaching the first frame not belonging to either. Count the first encountered frame instead as the hot one. # {0} is a number, {1} is a number NumberOfGcThreadsRuleFactory_TEXT_INFO=The runtime used {0} GC threads on a machine with {1} CPU cores. NumberOfGcThreadsRuleFactory_TEXT_INFO_LONG=It is not optimal to use more GC threads than available cores. Removing the '-XX:ParallelGCThreads' flag will allow the JVM to set the number of GC threads automatically.