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.io; 34 35 import java.text.MessageFormat; 36 import java.util.Arrays; 37 import java.util.Collection; 38 import java.util.List; 39 import java.util.concurrent.Callable; 40 import java.util.concurrent.FutureTask; 41 import java.util.concurrent.RunnableFuture; 42 43 import org.owasp.encoder.Encode; 44 45 import org.openjdk.jmc.common.IDisplayable; 46 import org.openjdk.jmc.common.item.Aggregators; 47 import org.openjdk.jmc.common.item.IItem; 48 import org.openjdk.jmc.common.item.IItemCollection; 49 import org.openjdk.jmc.common.unit.IQuantity; 50 import org.openjdk.jmc.common.unit.UnitLookup; 51 import org.openjdk.jmc.common.util.IPreferenceValueProvider; 52 import org.openjdk.jmc.common.util.TypedPreference; 53 import org.openjdk.jmc.flightrecorder.JfrAttributes; 54 import org.openjdk.jmc.flightrecorder.jdk.JdkAttributes; 55 import org.openjdk.jmc.flightrecorder.jdk.JdkFilters; 56 import org.openjdk.jmc.flightrecorder.jdk.JdkQueries; 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.Severity; 61 import org.openjdk.jmc.flightrecorder.rules.jdk.messages.internal.Messages; 62 import org.openjdk.jmc.flightrecorder.rules.util.JfrRuleTopics; 63 import org.openjdk.jmc.flightrecorder.rules.util.RulesToolkit; 64 import org.openjdk.jmc.flightrecorder.rules.util.RulesToolkit.EventAvailability; 65 66 public class FileReadRule implements IRule { 67 68 public static final TypedPreference<IQuantity> READ_WARNING_LIMIT = new TypedPreference<>( 69 "io.file.read.warning.limit", //$NON-NLS-1$ 70 Messages.getString(Messages.FileReadRule_CONFIG_WARNING_LIMIT), 71 Messages.getString(Messages.FileReadRule_CONFIG_WARNING_LIMIT_LONG), UnitLookup.TIMESPAN, 72 UnitLookup.MILLISECOND.quantity(4000)); 73 74 private static final List<TypedPreference<?>> CONFIG_ATTRIBUTES = Arrays 75 .<TypedPreference<?>> asList(READ_WARNING_LIMIT); 76 private static final String RESULT_ID = "FileRead"; //$NON-NLS-1$ 77 78 private Result getResult(IItemCollection items, IPreferenceValueProvider vp) { 79 EventAvailability eventAvailability = RulesToolkit.getEventAvailability(items, JdkTypeIDs.FILE_READ); 80 if (eventAvailability == EventAvailability.UNAVAILABLE || eventAvailability == EventAvailability.DISABLED) { 81 return RulesToolkit.getEventAvailabilityResult(this, items, eventAvailability, JdkTypeIDs.FILE_READ); 82 } 83 84 IQuantity warningLimit = vp.getPreferenceValue(READ_WARNING_LIMIT); 85 IQuantity infoLimit = warningLimit.multiply(0.5); 86 87 IItem longestEvent = items.apply(JdkFilters.FILE_READ) 88 .getAggregate(Aggregators.itemWithMax(JfrAttributes.DURATION)); 89 90 // Aggregate of all file read events - if null, then we had no events 91 if (longestEvent == null) { 92 return new Result(this, 0, Messages.getString(Messages.FileReadRuleFactory_TEXT_NO_EVENTS), null, 93 JdkQueries.FILE_READ); 94 } 95 IQuantity longestDuration = RulesToolkit.getValue(longestEvent, JfrAttributes.DURATION); 96 String peakDuration = longestDuration.displayUsing(IDisplayable.AUTO); 97 double score = RulesToolkit.mapExp100(longestDuration.doubleValueIn(UnitLookup.SECOND), 98 infoLimit.doubleValueIn(UnitLookup.SECOND), warningLimit.doubleValueIn(UnitLookup.SECOND)); 99 100 if (Severity.get(score) == Severity.WARNING || Severity.get(score) == Severity.INFO) { 101 String fileName = sanitizeFileName(RulesToolkit.getValue(longestEvent, JdkAttributes.IO_PATH)); 102 String amountRead = RulesToolkit.getValue(longestEvent, JdkAttributes.IO_FILE_BYTES_READ) 103 .displayUsing(IDisplayable.AUTO); 104 return new Result(this, score, 105 MessageFormat.format(Messages.getString(Messages.FileReadRuleFactory_TEXT_WARN), peakDuration), 106 MessageFormat.format(Messages.getString(Messages.FileReadRuleFactory_TEXT_WARN_LONG), peakDuration, 107 fileName, amountRead), 108 JdkQueries.FILE_READ); 109 } 110 return new Result(this, score, 111 MessageFormat.format(Messages.getString(Messages.FileReadRuleFactory_TEXT_OK), peakDuration), null, 112 JdkQueries.FILE_READ); 113 } 114 115 static String sanitizeFileName(String fileName) { 116 if (fileName == null || fileName.isEmpty()) { 117 return Encode.forHtml(Messages.getString(Messages.General_UNKNOWN_FILE_NAME)); 118 } 119 return Encode.forHtml(fileName); 120 } 121 122 @Override 123 public RunnableFuture<Result> evaluate(final IItemCollection items, final IPreferenceValueProvider valueProvider) { 124 FutureTask<Result> evaluationTask = new FutureTask<>(new Callable<Result>() { 125 @Override 126 public Result call() throws Exception { 127 return getResult(items, valueProvider); 128 } 129 }); 130 return evaluationTask; 131 } 132 133 @Override 134 public Collection<TypedPreference<?>> getConfigurationAttributes() { 135 return CONFIG_ATTRIBUTES; 136 } 137 138 @Override 139 public String getId() { 140 return RESULT_ID; 141 } 142 143 @Override 144 public String getName() { 145 return Messages.getString(Messages.FileReadRuleFactory_RULE_NAME); 146 } 147 148 @Override 149 public String getTopic() { 150 return JfrRuleTopics.FILE_IO_TOPIC; 151 } 152 }