1 /* 2 * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. 3 * 4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 5 * 6 * The contents of this file are subject to the terms of either the Universal Permissive License 7 * v 1.0 as shown at http://oss.oracle.com/licenses/upl 8 * 9 * or the following license: 10 * 11 * Redistribution and use in source and binary forms, with or without modification, are permitted 12 * provided that the following conditions are met: 13 * 14 * 1. Redistributions of source code must retain the above copyright notice, this list of conditions 15 * and the following disclaimer. 16 * 17 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of 18 * conditions and the following disclaimer in the documentation and/or other materials provided with 19 * the distribution. 20 * 21 * 3. Neither the name of the copyright holder nor the names of its contributors may be used to 22 * endorse or promote products derived from this software without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR 25 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 26 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 27 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 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.memory; 34 35 import static org.openjdk.jmc.common.unit.UnitLookup.MILLISECOND; 36 import static org.openjdk.jmc.common.unit.UnitLookup.TIMESPAN; 37 38 import java.text.MessageFormat; 39 import java.util.Arrays; 40 import java.util.Collection; 41 import java.util.List; 42 import java.util.concurrent.Callable; 43 import java.util.concurrent.FutureTask; 44 import java.util.concurrent.RunnableFuture; 45 46 import org.openjdk.jmc.common.IDisplayable; 47 import org.openjdk.jmc.common.item.Aggregators; 48 import org.openjdk.jmc.common.item.IItemCollection; 49 import org.openjdk.jmc.common.item.ItemFilters; 50 import org.openjdk.jmc.common.unit.IQuantity; 51 import org.openjdk.jmc.common.unit.UnitLookup; 52 import org.openjdk.jmc.common.util.IPreferenceValueProvider; 53 import org.openjdk.jmc.common.util.TypedPreference; 54 import org.openjdk.jmc.flightrecorder.JfrAttributes; 55 import org.openjdk.jmc.flightrecorder.jdk.JdkAggregators; 56 import org.openjdk.jmc.flightrecorder.jdk.JdkAttributes; 57 import org.openjdk.jmc.flightrecorder.jdk.JdkTypeIDs; 58 import org.openjdk.jmc.flightrecorder.rules.IRule; 59 import org.openjdk.jmc.flightrecorder.rules.Result; 60 import org.openjdk.jmc.flightrecorder.rules.jdk.messages.internal.Messages; 61 import org.openjdk.jmc.flightrecorder.rules.util.JfrRuleTopics; 62 import org.openjdk.jmc.flightrecorder.rules.util.RulesToolkit; 63 import org.openjdk.jmc.flightrecorder.rules.util.RulesToolkit.EventAvailability; 64 65 public class LongGcPauseRule implements IRule { 66 67 private static final String RESULT_ID = "LongGcPause"; //$NON-NLS-1$ 68 69 public static final TypedPreference<IQuantity> GC_PAUSE_INFO_LIMIT = new TypedPreference<>("gc.pause.info.limit", //$NON-NLS-1$ 70 Messages.getString(Messages.LongGcPauseRule_CONFIG_INFO_LIMIT), 71 Messages.getString(Messages.LongGcPauseRule_CONFIG_INFO_LIMIT_LONG), TIMESPAN, 72 UnitLookup.MILLISECOND.quantity(1000)); 73 74 private static final List<TypedPreference<?>> CONFIG_ATTRIBUTES = Arrays 75 .<TypedPreference<?>> asList(GC_PAUSE_INFO_LIMIT); 76 77 private Result getResult(IItemCollection items, IPreferenceValueProvider vp) { 78 EventAvailability eventAvailability = RulesToolkit.getEventAvailability(items, JdkTypeIDs.GC_PAUSE, 79 JdkTypeIDs.GC_CONF, JdkTypeIDs.HEAP_CONF, JdkTypeIDs.GC_PAUSE_L1); 80 if (eventAvailability == EventAvailability.DISABLED || eventAvailability == EventAvailability.UNKNOWN) { 81 return RulesToolkit.getEventAvailabilityResult(this, items, EventAvailability.DISABLED, JdkTypeIDs.GC_PAUSE, 82 JdkTypeIDs.GC_CONF, JdkTypeIDs.HEAP_CONF, JdkTypeIDs.GC_PAUSE_L1); 83 } 84 IQuantity maxPause = items.getAggregate(JdkAggregators.LONGEST_GC_PAUSE); 85 if (maxPause != null) { 86 String message = MessageFormat.format(Messages.getString(Messages.LongGcPauseRuleFactory_TEXT_INFO), 87 maxPause.displayUsing(IDisplayable.AUTO)); 88 double gcPauseScore = RulesToolkit.mapExp74(maxPause.doubleValueIn(MILLISECOND), 89 vp.getPreferenceValue(GC_PAUSE_INFO_LIMIT).doubleValueIn(MILLISECOND)); 90 String longMessage = message; 91 if (gcPauseScore >= 25) { 92 longMessage = appendMessage(longMessage, getLivesetMessage(items)); 93 longMessage = appendMessage(longMessage, getSemiRefsMessage(items)); 94 longMessage = appendMessage(longMessage, getCollectorMessage(items)); 95 } 96 return new Result(this, gcPauseScore, message, longMessage); 97 } 98 return new Result(this, 0, Messages.getString(Messages.LongGcPauseRuleFactory_TEXT_OK)); 99 } 100 101 @Override 102 public RunnableFuture<Result> evaluate(final IItemCollection items, final IPreferenceValueProvider valueProvider) { 103 FutureTask<Result> evaluationTask = new FutureTask<>(new Callable<Result>() { 104 @Override 105 public Result call() throws Exception { 106 return getResult(items, valueProvider); 107 } 108 }); 109 return evaluationTask; 110 } 111 112 private static String appendMessage(String message, String newMessage) { 113 if (message == null) { 114 return newMessage; 115 } else if (newMessage == null) { 116 return message; 117 } else { 118 return message + " " + newMessage; //$NON-NLS-1$ 119 } 120 } 121 122 private static String getCollectorMessage(IItemCollection items) { 123 CollectorType oldCollectorType = CollectorType.getOldCollectorType(items); 124 if (oldCollectorType != CollectorType.G1_OLD) { 125 return Messages.getString(Messages.LongGcPauseRuleFactory_TEXT_INFO_G1); 126 } 127 return null; 128 } 129 130 private static String getLivesetMessage(IItemCollection items) { 131 IQuantity liveSet = items.getAggregate(JdkAggregators.AVG_HEAP_USED_AFTER_GC); 132 IQuantity maxMx = items.getAggregate(JdkAggregators.HEAP_CONF_MAX_SIZE); 133 // If liveset is low ( < 50% ), suggest lowering mx. 134 if (liveSet != null && maxMx != null) { 135 int live = (int) (liveSet.ratioTo(maxMx) * 100); 136 if (live < 50) { 137 return MessageFormat.format(Messages.getString(Messages.LongGcPauseRuleFactory_TEXT_INFO_MX), 138 liveSet.displayUsing(IDisplayable.AUTO), maxMx.displayUsing(IDisplayable.AUTO)); 139 } 140 } 141 return null; 142 } 143 144 private static String getSemiRefsMessage(IItemCollection items) { 145 IQuantity aggregate = items 146 .getAggregate(Aggregators.filter(Aggregators.max(JdkTypeIDs.GC_PAUSE_L1, JfrAttributes.DURATION), 147 ItemFilters.equals(JdkAttributes.GC_PHASE_NAME, "References"))); //$NON-NLS-1$ 148 if (aggregate == null) { 149 return null; 150 } 151 int max = aggregate.compareTo(UnitLookup.MILLISECOND.quantity(50)); 152 if (max >= 50) { // if semirefs part of a gc takes longer than 50ms then we should inform the user 153 return Messages.getString(Messages.LongGcPauseRuleFactory_TEXT_INFO_REFERENCES); 154 } 155 return null; 156 } 157 158 @Override 159 public Collection<TypedPreference<?>> getConfigurationAttributes() { 160 return CONFIG_ATTRIBUTES; 161 } 162 163 @Override 164 public String getId() { 165 return RESULT_ID; 166 } 167 168 @Override 169 public String getName() { 170 return Messages.getString(Messages.LongGcPauseRuleFactory_RULE_NAME); 171 } 172 173 @Override 174 public String getTopic() { 175 return JfrRuleTopics.GARBAGE_COLLECTION_TOPIC; 176 } 177 }