< prev index next >

core/org.openjdk.jmc.flightrecorder.rules.jdk/src/main/java/org/openjdk/jmc/flightrecorder/rules/jdk/latency/MethodProfilingRule.java

Print this page




  30  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
  31  * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  32  */
  33 package org.openjdk.jmc.flightrecorder.rules.jdk.latency;
  34 
  35 import java.text.MessageFormat;
  36 import java.util.ArrayList;
  37 import java.util.Arrays;
  38 import java.util.Collection;
  39 import java.util.Collections;
  40 import java.util.Comparator;
  41 import java.util.HashMap;
  42 import java.util.Iterator;
  43 import java.util.List;
  44 import java.util.Map;
  45 import java.util.Map.Entry;
  46 import java.util.Set;
  47 import java.util.concurrent.Callable;
  48 import java.util.concurrent.FutureTask;
  49 import java.util.concurrent.RunnableFuture;


  50 
  51 import org.openjdk.jmc.common.IDisplayable;

  52 import org.openjdk.jmc.common.IMCMethod;
  53 import org.openjdk.jmc.common.IMCStackTrace;
  54 import org.openjdk.jmc.common.item.Aggregators;
  55 import org.openjdk.jmc.common.item.Aggregators.CountConsumer;
  56 import org.openjdk.jmc.common.item.GroupingAggregator;
  57 import org.openjdk.jmc.common.item.GroupingAggregator.GroupEntry;
  58 import org.openjdk.jmc.common.item.IAggregator;
  59 import org.openjdk.jmc.common.item.IItem;
  60 import org.openjdk.jmc.common.item.IItemCollection;
  61 import org.openjdk.jmc.common.item.IItemFilter;
  62 import org.openjdk.jmc.common.item.IItemIterable;
  63 import org.openjdk.jmc.common.item.IMemberAccessor;
  64 import org.openjdk.jmc.common.item.IType;
  65 import org.openjdk.jmc.common.item.ItemFilters;
  66 import org.openjdk.jmc.common.unit.IQuantity;
  67 import org.openjdk.jmc.common.unit.IRange;
  68 import org.openjdk.jmc.common.unit.QuantityConversionException;
  69 import org.openjdk.jmc.common.unit.QuantityRange;
  70 import org.openjdk.jmc.common.unit.UnitLookup;
  71 import org.openjdk.jmc.common.util.FormatToolkit;
  72 import org.openjdk.jmc.common.util.IPreferenceValueProvider;

  73 import org.openjdk.jmc.common.util.Pair;
  74 import org.openjdk.jmc.common.util.TypedPreference;
  75 import org.openjdk.jmc.flightrecorder.JfrAttributes;
  76 import org.openjdk.jmc.flightrecorder.jdk.JdkAttributes;
  77 import org.openjdk.jmc.flightrecorder.jdk.JdkFilters;
  78 import org.openjdk.jmc.flightrecorder.jdk.JdkTypeIDs;
  79 import org.openjdk.jmc.flightrecorder.rules.IRule;
  80 import org.openjdk.jmc.flightrecorder.rules.Result;
  81 import org.openjdk.jmc.flightrecorder.rules.jdk.dataproviders.MethodProfilingDataProvider;
  82 import org.openjdk.jmc.flightrecorder.rules.jdk.messages.internal.Messages;
  83 import org.openjdk.jmc.flightrecorder.rules.util.JfrRuleTopics;
  84 import org.openjdk.jmc.flightrecorder.rules.util.RulesToolkit;
  85 import org.openjdk.jmc.flightrecorder.rules.util.RulesToolkit.EventAvailability;
  86 import org.openjdk.jmc.flightrecorder.rules.util.SlidingWindowToolkit;
  87 import org.openjdk.jmc.flightrecorder.rules.util.SlidingWindowToolkit.IUnorderedWindowVisitor;
  88 import org.openjdk.jmc.flightrecorder.stacktrace.StacktraceModel;
  89 
  90 /**
  91  * Rule that calculates the top method balance in a sliding window throughout the recording with a
  92  * relevance calculated by the ratio of samples to maximum samples for that period.
  93  */
  94 public class MethodProfilingRule implements IRule {
  95 
  96         /**
  97          * Constant value of the maximum number of samples the JVM attempts per sampling period.
  98          */
  99         private static final double SAMPLES_PER_PERIOD = 5;
 100 
 101         /**
 102          * Constant value of the maximum number of stack frames to display for the hottest path.
 103          */
 104         private static final int MAX_STACK_DEPTH = 10;
 105 
 106         /**
 107          * A simple class for storing execution sample period settings, allowing the sliding window to
 108          * get the correct samples for each time slice.


 151                         this.method = method;
 152                         this.path = path;
 153                         this.ratioOfAllPossibleSamples = ratio;
 154                         this.window = window;
 155                 }
 156 
 157                 @Override
 158                 public String toString() {
 159                         return FormatToolkit.getHumanReadable(method, false, false, true, true, true, false) + " (" //$NON-NLS-1$
 160                                         + ratioOfAllPossibleSamples.displayUsing(IDisplayable.AUTO) + ") " //$NON-NLS-1$
 161                                         + window.displayUsing(IDisplayable.AUTO);
 162                 }
 163         }
 164 
 165         private static final String RESULT_ID = "MethodProfiling"; //$NON-NLS-1$
 166         public static final TypedPreference<IQuantity> WINDOW_SIZE = new TypedPreference<>(
 167                         "method.profiling.evaluation.window.size", //$NON-NLS-1$
 168                         Messages.getString(Messages.MethodProfilingRule_WINDOW_SIZE),
 169                         Messages.getString(Messages.MethodProfilingRule_WINDOW_SIZE_DESC), UnitLookup.TIMESPAN,
 170                         UnitLookup.SECOND.quantity(30));
 171         private static final List<TypedPreference<?>> CONFIG_ATTRIBUTES = Arrays.<TypedPreference<?>> asList(WINDOW_SIZE);





 172 
 173         /**
 174          * Private Callable implementation specifically used to avoid storing the FutureTask as a field.
 175          */
 176         private class MethodProfilingCallable implements Callable<Result> {
 177                 private FutureTask<Result> evaluationTask = null;
 178                 private IItemCollection items;
 179                 private IPreferenceValueProvider valueProvider;
 180 
 181                 private MethodProfilingCallable(IItemCollection items, IPreferenceValueProvider valueProvider) {
 182                         this.items = items;
 183                         this.valueProvider = valueProvider;
 184                 }
 185 
 186                 @Override
 187                 public Result call() throws Exception {
 188                         return getResult(items, valueProvider, evaluationTask);
 189                 }
 190 
 191                 void setTask(FutureTask<Result> task) {


 200                 callable.setTask(evaluationTask);
 201                 return evaluationTask;
 202         }
 203 
 204         private Result getResult(
 205                 IItemCollection items, IPreferenceValueProvider valueProvider, FutureTask<Result> evaluationTask) {
 206                 EventAvailability eventAvailability = RulesToolkit.getEventAvailability(items, JdkTypeIDs.EXECUTION_SAMPLE,
 207                                 JdkTypeIDs.RECORDING_SETTING);
 208                 if (eventAvailability != EventAvailability.AVAILABLE) {
 209                         return RulesToolkit.getEventAvailabilityResult(this, items, eventAvailability, JdkTypeIDs.EXECUTION_SAMPLE,
 210                                         JdkTypeIDs.RECORDING_SETTING);
 211                 }
 212 
 213                 PeriodRangeMap settings = new PeriodRangeMap();
 214                 IItemFilter settingsFilter = RulesToolkit.getSettingsFilter(RulesToolkit.REC_SETTING_NAME_PERIOD,
 215                                 JdkTypeIDs.EXECUTION_SAMPLE);
 216                 populateSettingsMap(items.apply(settingsFilter), settings);
 217 
 218                 IQuantity windowSize = valueProvider.getPreferenceValue(WINDOW_SIZE);
 219                 IQuantity slideSize = UnitLookup.SECOND.quantity(windowSize.ratioTo(UnitLookup.SECOND.quantity(2)));
 220 







 221                 List<MethodProfilingWindowResult> windowResults = new ArrayList<>();
 222                 IUnorderedWindowVisitor visitor = createWindowVisitor(settings, settingsFilter, windowSize, windowResults,
 223                                 evaluationTask);
 224                 SlidingWindowToolkit.slidingWindowUnordered(visitor, items, windowSize, slideSize);
 225                 // 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.
 226                 if (windowResults.isEmpty()) {
 227                         return RulesToolkit.getNotApplicableResult(this,
 228                                         Messages.getString(Messages.HotMethodsRuleFactory_NOT_ENOUGH_SAMPLES));
 229                 }
 230                 Pair<MethodProfilingWindowResult, Map<IMCStackTrace, MethodProfilingWindowResult>> interestingMethods = getInterestingMethods(
 231                                 windowResults);
 232                 Map<IMCStackTrace, MethodProfilingWindowResult> percentByMethod = interestingMethods.right;
 233                 MethodProfilingWindowResult mostInterestingResult = interestingMethods.left;
 234                 if (mostInterestingResult == null) { // Couldn't find any interesting methods
 235                         return new Result(this, 0, Messages.getString(Messages.HotMethodsRuleFactory_TEXT_OK));
 236                 }
 237                 double mappedScore = performSigmoidMap(
 238                                 mostInterestingResult.ratioOfAllPossibleSamples.doubleValueIn(UnitLookup.PERCENT_UNITY));
 239 
 240                 Result result = null;
 241                 if (mappedScore < 25) {
 242                         result = new Result(this, mappedScore, Messages.getString(Messages.HotMethodsRuleFactory_TEXT_OK));
 243                 } else {


 303         /**
 304          * Creates an IUnorderedWindowVisitor that is called on each slice in the recording and
 305          * generates the scores for each slice and places them in the rawScores list. The given
 306          * parameters that are also given to the slidingWindowUnordered call must be the same as in this
 307          * call.
 308          *
 309          * @param settings
 310          *            the settings map with all the times the execution sample event has a change of
 311          *            periodicity
 312          * @param settingsFilter
 313          *            the filter used to select the recording setting for the execution sample event
 314          * @param windowSize
 315          *            the size of the sliding window
 316          * @param rawScores
 317          *            the list of raw scores that will be populated by this visitor
 318          * @return an IUnorderedWindowVisitor implementation that will populate the rawScores list with
 319          *         raw score values
 320          */
 321         private IUnorderedWindowVisitor createWindowVisitor(
 322                 final PeriodRangeMap settings, final IItemFilter settingsFilter, final IQuantity windowSize,
 323                 final List<MethodProfilingWindowResult> rawScores, final FutureTask<Result> evaluationTask) {
 324                 return new IUnorderedWindowVisitor() {
 325                         @Override
 326                         public void visitWindow(IItemCollection items, IQuantity startTime, IQuantity endTime) {
 327                                 IRange<IQuantity> windowRange = QuantityRange.createWithEnd(startTime, endTime);
 328                                 if (RulesToolkit.getSettingMaxPeriod(items, JdkTypeIDs.EXECUTION_SAMPLE) == null) {
 329                                         Pair<IQuantity, IMCStackTrace> resultPair = performCalculation(items, settings.getSetting(startTime));
 330                                         if (resultPair != null) {
 331                                                 rawScores.add(new MethodProfilingWindowResult(resultPair.right.getFrames().get(0).getMethod(), resultPair.right, resultPair.left, windowRange));
 332                                         }
 333                                 } else {
 334                                         Set<IQuantity> settingTimes = items.apply(settingsFilter)
 335                                                         .getAggregate(Aggregators.distinct(JfrAttributes.START_TIME));
 336                                         IQuantity start = startTime;
 337                                         List<Pair<IQuantity, IMCStackTrace>> scores = new ArrayList<>(settingTimes.size());
 338                                         for (IQuantity settingTime : settingTimes) {
 339                                                 IItemFilter window = ItemFilters.interval(JfrAttributes.END_TIME, start, true, settingTime,
 340                                                                 true);
 341                                                 scores.add(performCalculation(items.apply(window), settings.getSetting(start)));
 342                                                 start = settingTime;
 343                                         }


 386                                 final IMCStackTrace[] maxPath = new IMCStackTrace[1];
 387                                 // Using this GroupingAggregator because it's the only way to extract the keys from the aggregation along with values
 388                                 IAggregator<IQuantity, ?> aggregator = GroupingAggregator.build("", "", //$NON-NLS-1$ //$NON_NLS_2$
 389                                                 MethodProfilingDataProvider.PATH_ACCESSOR_FACTORY, Aggregators.count(),
 390                                                 new GroupingAggregator.IGroupsFinisher<IQuantity, IMCStackTrace, CountConsumer>() {
 391 
 392                                                         @Override
 393                                                         public IType<IQuantity> getValueType() {
 394                                                                 return UnitLookup.NUMBER;
 395                                                         }
 396 
 397                                                         @Override
 398                                                         public IQuantity getValue(Iterable<? extends GroupEntry<IMCStackTrace, CountConsumer>> groupEntries) {
 399                                                                 HashMap<IMCMethod, IQuantity> map = new HashMap<>();
 400                                                                 HashMap<IMCMethod, IMCStackTrace> pathMap = new HashMap<>();
 401                                                                 int total = 0;
 402                                                                 // When we group by stack trace we can run into situations where the top frames are otherwise the same
 403                                                                 // for our purposes (finding the hottest method), but they differ by BCI, throwing off the count.
 404                                                                 // so we should collect further on the method for the top frame.
 405                                                                 for (GroupEntry<IMCStackTrace, CountConsumer> group : groupEntries) {

 406                                                                         total += group.getConsumer().getCount();
 407                                                                         IMCMethod topFrameMethod = group.getKey().getFrames().get(0).getMethod();

 408                                                                         if (map.get(topFrameMethod) == null) {
 409                                                                                 map.put(topFrameMethod, UnitLookup.NUMBER_UNITY.quantity(group.getConsumer().getCount()));
 410                                                                                 pathMap.put(topFrameMethod, group.getKey());
 411                                                                         } else {
 412                                                                                 IQuantity old = map.get(topFrameMethod);
 413                                                                                 map.put(topFrameMethod, old.add(UnitLookup.NUMBER_UNITY.quantity(group.getConsumer().getCount())));
 414                                                                         }
 415                                                                 }

 416                                                                 if (!pathMap.isEmpty() && !map.isEmpty()) {
 417                                                                         Entry<IMCMethod, IQuantity> topEntry = Collections.max(map.entrySet(), new Comparator<Entry<IMCMethod, IQuantity>>() {
 418                                                                                 @Override
 419                                                                                 public int compare(Entry<IMCMethod, IQuantity> arg0,
 420                                                                                                 Entry<IMCMethod, IQuantity> arg1) {
 421                                                                                         return arg0.getValue().compareTo(arg1.getValue());
 422                                                                                 }
 423                                                                         });
 424                                                                         maxPath[0] = pathMap.get(topEntry.getKey());
 425                                                                         maxMethod[0] = topEntry.getKey();
 426                                                                         return topEntry.getValue().multiply(1d/total);
 427                                                                 }
 428                                                                 return UnitLookup.NUMBER_UNITY.quantity(0);
















 429                                                         }
 430                                 });
 431 
 432                                 IQuantity maxRatio = filteredItems.getAggregate(aggregator);
 433                                 Pair<IQuantity, IMCStackTrace> result = null;
 434                                 if (maxMethod[0] != null && maxRatio != null && period != null) { // ignoring if there are no samples or if we don't yet know the periodicity
 435                                         double periodsPerSecond = 1 / period.doubleValueIn(UnitLookup.SECOND);
 436                                         double maxSamplesPerSecond = SAMPLES_PER_PERIOD * periodsPerSecond;
 437                                         double samplesInPeriod = items
 438                                                         .getAggregate(Aggregators.count(ItemFilters.type(JdkTypeIDs.EXECUTION_SAMPLE)))
 439                                                         .doubleValueIn(UnitLookup.NUMBER_UNITY);
 440                                         double maxSamplesInPeriod = maxSamplesPerSecond * windowSize.doubleValueIn(UnitLookup.SECOND);
 441                                         double relevancy = samplesInPeriod / maxSamplesInPeriod;
 442                                         double highestRatioOfSamples = maxRatio.doubleValueIn(UnitLookup.NUMBER_UNITY);
 443                                         IQuantity percentOfAllPossibleSamples = UnitLookup.PERCENT_UNITY
 444                                                         .quantity(highestRatioOfSamples * relevancy);
 445                                         result = new Pair<>(percentOfAllPossibleSamples, maxPath[0]);
 446                                 }
 447                                 return result;
 448                         }




  30  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
  31  * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  32  */
  33 package org.openjdk.jmc.flightrecorder.rules.jdk.latency;
  34 
  35 import java.text.MessageFormat;
  36 import java.util.ArrayList;
  37 import java.util.Arrays;
  38 import java.util.Collection;
  39 import java.util.Collections;
  40 import java.util.Comparator;
  41 import java.util.HashMap;
  42 import java.util.Iterator;
  43 import java.util.List;
  44 import java.util.Map;
  45 import java.util.Map.Entry;
  46 import java.util.Set;
  47 import java.util.concurrent.Callable;
  48 import java.util.concurrent.FutureTask;
  49 import java.util.concurrent.RunnableFuture;
  50 import java.util.regex.Matcher;
  51 import java.util.regex.Pattern;
  52 
  53 import org.openjdk.jmc.common.IDisplayable;
  54 import org.openjdk.jmc.common.IMCFrame;
  55 import org.openjdk.jmc.common.IMCMethod;
  56 import org.openjdk.jmc.common.IMCStackTrace;
  57 import org.openjdk.jmc.common.item.Aggregators;
  58 import org.openjdk.jmc.common.item.Aggregators.CountConsumer;
  59 import org.openjdk.jmc.common.item.GroupingAggregator;
  60 import org.openjdk.jmc.common.item.GroupingAggregator.GroupEntry;
  61 import org.openjdk.jmc.common.item.IAggregator;
  62 import org.openjdk.jmc.common.item.IItem;
  63 import org.openjdk.jmc.common.item.IItemCollection;
  64 import org.openjdk.jmc.common.item.IItemFilter;
  65 import org.openjdk.jmc.common.item.IItemIterable;
  66 import org.openjdk.jmc.common.item.IMemberAccessor;
  67 import org.openjdk.jmc.common.item.IType;
  68 import org.openjdk.jmc.common.item.ItemFilters;
  69 import org.openjdk.jmc.common.unit.IQuantity;
  70 import org.openjdk.jmc.common.unit.IRange;
  71 import org.openjdk.jmc.common.unit.QuantityConversionException;
  72 import org.openjdk.jmc.common.unit.QuantityRange;
  73 import org.openjdk.jmc.common.unit.UnitLookup;
  74 import org.openjdk.jmc.common.util.FormatToolkit;
  75 import org.openjdk.jmc.common.util.IPreferenceValueProvider;
  76 import org.openjdk.jmc.common.util.MCStackTrace;
  77 import org.openjdk.jmc.common.util.Pair;
  78 import org.openjdk.jmc.common.util.TypedPreference;
  79 import org.openjdk.jmc.flightrecorder.JfrAttributes;
  80 import org.openjdk.jmc.flightrecorder.jdk.JdkAttributes;
  81 import org.openjdk.jmc.flightrecorder.jdk.JdkFilters;
  82 import org.openjdk.jmc.flightrecorder.jdk.JdkTypeIDs;
  83 import org.openjdk.jmc.flightrecorder.rules.IRule;
  84 import org.openjdk.jmc.flightrecorder.rules.Result;
  85 import org.openjdk.jmc.flightrecorder.rules.jdk.dataproviders.MethodProfilingDataProvider;
  86 import org.openjdk.jmc.flightrecorder.rules.jdk.messages.internal.Messages;
  87 import org.openjdk.jmc.flightrecorder.rules.util.JfrRuleTopics;
  88 import org.openjdk.jmc.flightrecorder.rules.util.RulesToolkit;
  89 import org.openjdk.jmc.flightrecorder.rules.util.RulesToolkit.EventAvailability;
  90 import org.openjdk.jmc.flightrecorder.rules.util.SlidingWindowToolkit;
  91 import org.openjdk.jmc.flightrecorder.rules.util.SlidingWindowToolkit.IUnorderedWindowVisitor;

  92 
  93 /**
  94  * Rule that calculates the top method balance in a sliding window throughout the recording with a
  95  * relevance calculated by the ratio of samples to maximum samples for that period.
  96  */
  97 public class MethodProfilingRule implements IRule {
  98 
  99         /**
 100          * Constant value of the maximum number of samples the JVM attempts per sampling period.
 101          */
 102         private static final double SAMPLES_PER_PERIOD = 5;
 103 
 104         /**
 105          * Constant value of the maximum number of stack frames to display for the hottest path.
 106          */
 107         private static final int MAX_STACK_DEPTH = 10;
 108 
 109         /**
 110          * A simple class for storing execution sample period settings, allowing the sliding window to
 111          * get the correct samples for each time slice.


 154                         this.method = method;
 155                         this.path = path;
 156                         this.ratioOfAllPossibleSamples = ratio;
 157                         this.window = window;
 158                 }
 159 
 160                 @Override
 161                 public String toString() {
 162                         return FormatToolkit.getHumanReadable(method, false, false, true, true, true, false) + " (" //$NON-NLS-1$
 163                                         + ratioOfAllPossibleSamples.displayUsing(IDisplayable.AUTO) + ") " //$NON-NLS-1$
 164                                         + window.displayUsing(IDisplayable.AUTO);
 165                 }
 166         }
 167 
 168         private static final String RESULT_ID = "MethodProfiling"; //$NON-NLS-1$
 169         public static final TypedPreference<IQuantity> WINDOW_SIZE = new TypedPreference<>(
 170                         "method.profiling.evaluation.window.size", //$NON-NLS-1$
 171                         Messages.getString(Messages.MethodProfilingRule_WINDOW_SIZE),
 172                         Messages.getString(Messages.MethodProfilingRule_WINDOW_SIZE_DESC), UnitLookup.TIMESPAN,
 173                         UnitLookup.SECOND.quantity(30));
 174         public static final TypedPreference<String> EXCLUDED_PACKAGE_REGEXP = new TypedPreference<>(
 175                         "method.profiling.evaluation.excluded.package", //$NON-NLS-1$
 176                         Messages.getString(Messages.MethodProfilingRule_EXCLUDED_PACKAGES),
 177                         Messages.getString(Messages.MethodProfilingRule_EXCLUDED_PACKAGES_DESC),
 178                         UnitLookup.PLAIN_TEXT.getPersister(), "");
 179         private static final List<TypedPreference<?>> CONFIG_ATTRIBUTES = Arrays.<TypedPreference<?>> asList(WINDOW_SIZE, EXCLUDED_PACKAGE_REGEXP);
 180 
 181         /**
 182          * Private Callable implementation specifically used to avoid storing the FutureTask as a field.
 183          */
 184         private class MethodProfilingCallable implements Callable<Result> {
 185                 private FutureTask<Result> evaluationTask = null;
 186                 private IItemCollection items;
 187                 private IPreferenceValueProvider valueProvider;
 188 
 189                 private MethodProfilingCallable(IItemCollection items, IPreferenceValueProvider valueProvider) {
 190                         this.items = items;
 191                         this.valueProvider = valueProvider;
 192                 }
 193 
 194                 @Override
 195                 public Result call() throws Exception {
 196                         return getResult(items, valueProvider, evaluationTask);
 197                 }
 198 
 199                 void setTask(FutureTask<Result> task) {


 208                 callable.setTask(evaluationTask);
 209                 return evaluationTask;
 210         }
 211 
 212         private Result getResult(
 213                 IItemCollection items, IPreferenceValueProvider valueProvider, FutureTask<Result> evaluationTask) {
 214                 EventAvailability eventAvailability = RulesToolkit.getEventAvailability(items, JdkTypeIDs.EXECUTION_SAMPLE,
 215                                 JdkTypeIDs.RECORDING_SETTING);
 216                 if (eventAvailability != EventAvailability.AVAILABLE) {
 217                         return RulesToolkit.getEventAvailabilityResult(this, items, eventAvailability, JdkTypeIDs.EXECUTION_SAMPLE,
 218                                         JdkTypeIDs.RECORDING_SETTING);
 219                 }
 220 
 221                 PeriodRangeMap settings = new PeriodRangeMap();
 222                 IItemFilter settingsFilter = RulesToolkit.getSettingsFilter(RulesToolkit.REC_SETTING_NAME_PERIOD,
 223                                 JdkTypeIDs.EXECUTION_SAMPLE);
 224                 populateSettingsMap(items.apply(settingsFilter), settings);
 225 
 226                 IQuantity windowSize = valueProvider.getPreferenceValue(WINDOW_SIZE);
 227                 IQuantity slideSize = UnitLookup.SECOND.quantity(windowSize.ratioTo(UnitLookup.SECOND.quantity(2)));
 228                 String excludedPattern = valueProvider.getPreferenceValue(EXCLUDED_PACKAGE_REGEXP);
 229                 Pattern excludes;
 230                 try {
 231                         excludes = Pattern.compile(excludedPattern);
 232                 } catch (Exception e) {
 233                         // Make sure we don't blow up on an invalid pattern.
 234                         excludes = Pattern.compile("");
 235                 }
 236                 List<MethodProfilingWindowResult> windowResults = new ArrayList<>();
 237                 IUnorderedWindowVisitor visitor = createWindowVisitor(settings, settingsFilter, windowSize, windowResults,
 238                                 evaluationTask, excludes);
 239                 SlidingWindowToolkit.slidingWindowUnordered(visitor, items, windowSize, slideSize);
 240                 // 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.
 241                 if (windowResults.isEmpty()) {
 242                         return RulesToolkit.getNotApplicableResult(this,
 243                                         Messages.getString(Messages.HotMethodsRuleFactory_NOT_ENOUGH_SAMPLES));
 244                 }
 245                 Pair<MethodProfilingWindowResult, Map<IMCStackTrace, MethodProfilingWindowResult>> interestingMethods = getInterestingMethods(
 246                                 windowResults);
 247                 Map<IMCStackTrace, MethodProfilingWindowResult> percentByMethod = interestingMethods.right;
 248                 MethodProfilingWindowResult mostInterestingResult = interestingMethods.left;
 249                 if (mostInterestingResult == null) { // Couldn't find any interesting methods
 250                         return new Result(this, 0, Messages.getString(Messages.HotMethodsRuleFactory_TEXT_OK));
 251                 }
 252                 double mappedScore = performSigmoidMap(
 253                                 mostInterestingResult.ratioOfAllPossibleSamples.doubleValueIn(UnitLookup.PERCENT_UNITY));
 254 
 255                 Result result = null;
 256                 if (mappedScore < 25) {
 257                         result = new Result(this, mappedScore, Messages.getString(Messages.HotMethodsRuleFactory_TEXT_OK));
 258                 } else {


 318         /**
 319          * Creates an IUnorderedWindowVisitor that is called on each slice in the recording and
 320          * generates the scores for each slice and places them in the rawScores list. The given
 321          * parameters that are also given to the slidingWindowUnordered call must be the same as in this
 322          * call.
 323          *
 324          * @param settings
 325          *            the settings map with all the times the execution sample event has a change of
 326          *            periodicity
 327          * @param settingsFilter
 328          *            the filter used to select the recording setting for the execution sample event
 329          * @param windowSize
 330          *            the size of the sliding window
 331          * @param rawScores
 332          *            the list of raw scores that will be populated by this visitor
 333          * @return an IUnorderedWindowVisitor implementation that will populate the rawScores list with
 334          *         raw score values
 335          */
 336         private IUnorderedWindowVisitor createWindowVisitor(
 337                 final PeriodRangeMap settings, final IItemFilter settingsFilter, final IQuantity windowSize,
 338                 final List<MethodProfilingWindowResult> rawScores, final FutureTask<Result> evaluationTask, final Pattern excludes) {
 339                 return new IUnorderedWindowVisitor() {
 340                         @Override
 341                         public void visitWindow(IItemCollection items, IQuantity startTime, IQuantity endTime) {
 342                                 IRange<IQuantity> windowRange = QuantityRange.createWithEnd(startTime, endTime);
 343                                 if (RulesToolkit.getSettingMaxPeriod(items, JdkTypeIDs.EXECUTION_SAMPLE) == null) {
 344                                         Pair<IQuantity, IMCStackTrace> resultPair = performCalculation(items, settings.getSetting(startTime));
 345                                         if (resultPair != null) {
 346                                                 rawScores.add(new MethodProfilingWindowResult(resultPair.right.getFrames().get(0).getMethod(), resultPair.right, resultPair.left, windowRange));
 347                                         }
 348                                 } else {
 349                                         Set<IQuantity> settingTimes = items.apply(settingsFilter)
 350                                                         .getAggregate(Aggregators.distinct(JfrAttributes.START_TIME));
 351                                         IQuantity start = startTime;
 352                                         List<Pair<IQuantity, IMCStackTrace>> scores = new ArrayList<>(settingTimes.size());
 353                                         for (IQuantity settingTime : settingTimes) {
 354                                                 IItemFilter window = ItemFilters.interval(JfrAttributes.END_TIME, start, true, settingTime,
 355                                                                 true);
 356                                                 scores.add(performCalculation(items.apply(window), settings.getSetting(start)));
 357                                                 start = settingTime;
 358                                         }


 401                                 final IMCStackTrace[] maxPath = new IMCStackTrace[1];
 402                                 // Using this GroupingAggregator because it's the only way to extract the keys from the aggregation along with values
 403                                 IAggregator<IQuantity, ?> aggregator = GroupingAggregator.build("", "", //$NON-NLS-1$ //$NON_NLS_2$
 404                                                 MethodProfilingDataProvider.PATH_ACCESSOR_FACTORY, Aggregators.count(),
 405                                                 new GroupingAggregator.IGroupsFinisher<IQuantity, IMCStackTrace, CountConsumer>() {
 406 
 407                                                         @Override
 408                                                         public IType<IQuantity> getValueType() {
 409                                                                 return UnitLookup.NUMBER;
 410                                                         }
 411 
 412                                                         @Override
 413                                                         public IQuantity getValue(Iterable<? extends GroupEntry<IMCStackTrace, CountConsumer>> groupEntries) {
 414                                                                 HashMap<IMCMethod, IQuantity> map = new HashMap<>();
 415                                                                 HashMap<IMCMethod, IMCStackTrace> pathMap = new HashMap<>();
 416                                                                 int total = 0;
 417                                                                 // When we group by stack trace we can run into situations where the top frames are otherwise the same
 418                                                                 // for our purposes (finding the hottest method), but they differ by BCI, throwing off the count.
 419                                                                 // so we should collect further on the method for the top frame.
 420                                                                 for (GroupEntry<IMCStackTrace, CountConsumer> group : groupEntries) {
 421                                                                         IMCStackTrace trace = processPath(group.getKey());
 422                                                                         total += group.getConsumer().getCount();
 423                                                                         if (!trace.getFrames().isEmpty()) {
 424                                                                                 IMCMethod topFrameMethod = trace.getFrames().get(0).getMethod();
 425                                                                                 if (map.get(topFrameMethod) == null) {
 426                                                                                         map.put(topFrameMethod, UnitLookup.NUMBER_UNITY.quantity(group.getConsumer().getCount()));
 427                                                                                         pathMap.put(topFrameMethod, trace);
 428                                                                                 } else {
 429                                                                                         IQuantity old = map.get(topFrameMethod);
 430                                                                                         map.put(topFrameMethod, old.add(UnitLookup.NUMBER_UNITY.quantity(group.getConsumer().getCount())));
 431                                                                                 }
 432                                                                         }
 433                                                                 }
 434                                                                 if (!pathMap.isEmpty() && !map.isEmpty()) {
 435                                                                         Entry<IMCMethod, IQuantity> topEntry = Collections.max(map.entrySet(), new Comparator<Entry<IMCMethod, IQuantity>>() {
 436                                                                                 @Override
 437                                                                                 public int compare(Entry<IMCMethod, IQuantity> arg0,
 438                                                                                                 Entry<IMCMethod, IQuantity> arg1) {
 439                                                                                         return arg0.getValue().compareTo(arg1.getValue());
 440                                                                                 }
 441                                                                         });
 442                                                                         maxPath[0] = pathMap.get(topEntry.getKey());
 443                                                                         maxMethod[0] = topEntry.getKey();
 444                                                                         return topEntry.getValue().multiply(1d/total);
 445                                                                 }
 446                                                                 return UnitLookup.NUMBER_UNITY.quantity(0);
 447                                                         }
 448 
 449                                                         private IMCStackTrace processPath(IMCStackTrace path) {
 450                                                                 List<IMCFrame> frames = new ArrayList<>(path.getFrames());
 451                                                                 List<IMCFrame> framesToDrop = new ArrayList<IMCFrame>();
 452                                                                 // Drop any frames that match the excluded pattern, thereby treating the first non-matching frame that we encounter as the hot one.
 453                                                                 for (IMCFrame frame : frames) {
 454                                                                         Matcher m = excludes.matcher(FormatToolkit.getHumanReadable(frame.getMethod(), false, false, true, true, false, false));
 455                                                                         if (m.matches()) {
 456                                                                                 framesToDrop.add(frame);
 457                                                                         } else {
 458                                                                                 break;
 459                                                                         }
 460                                                                 }
 461                                                                 frames.removeAll(framesToDrop);
 462                                                                 return new MCStackTrace(frames, path.getTruncationState());
 463                                                         }
 464                                 });
 465 
 466                                 IQuantity maxRatio = filteredItems.getAggregate(aggregator);
 467                                 Pair<IQuantity, IMCStackTrace> result = null;
 468                                 if (maxMethod[0] != null && maxRatio != null && period != null) { // ignoring if there are no samples or if we don't yet know the periodicity
 469                                         double periodsPerSecond = 1 / period.doubleValueIn(UnitLookup.SECOND);
 470                                         double maxSamplesPerSecond = SAMPLES_PER_PERIOD * periodsPerSecond;
 471                                         double samplesInPeriod = items
 472                                                         .getAggregate(Aggregators.count(ItemFilters.type(JdkTypeIDs.EXECUTION_SAMPLE)))
 473                                                         .doubleValueIn(UnitLookup.NUMBER_UNITY);
 474                                         double maxSamplesInPeriod = maxSamplesPerSecond * windowSize.doubleValueIn(UnitLookup.SECOND);
 475                                         double relevancy = samplesInPeriod / maxSamplesInPeriod;
 476                                         double highestRatioOfSamples = maxRatio.doubleValueIn(UnitLookup.NUMBER_UNITY);
 477                                         IQuantity percentOfAllPossibleSamples = UnitLookup.PERCENT_UNITY
 478                                                         .quantity(highestRatioOfSamples * relevancy);
 479                                         result = new Pair<>(percentOfAllPossibleSamples, maxPath[0]);
 480                                 }
 481                                 return result;
 482                         }


< prev index next >