1 /* 2 * Copyright (c) 2016, 2021, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.jfr.internal.management; 27 28 import java.io.IOException; 29 import java.nio.file.Path; 30 import java.nio.file.Paths; 31 import java.time.Duration; 32 import java.time.Instant; 33 import java.util.Collections; 34 import java.util.List; 35 import java.util.Map; 36 import java.util.function.Consumer; 37 import java.security.AccessControlContext; 38 39 import jdk.jfr.Configuration; 40 import jdk.jfr.EventSettings; 41 import jdk.jfr.EventType; 42 import jdk.jfr.Recording; 43 import jdk.jfr.consumer.EventStream; 44 import jdk.jfr.internal.JVMSupport; 45 import jdk.jfr.internal.LogLevel; 46 import jdk.jfr.internal.LogTag; 47 import jdk.jfr.internal.Logger; 48 import jdk.jfr.internal.MetadataRepository; 49 import jdk.jfr.internal.PlatformRecording; 50 import jdk.jfr.internal.PrivateAccess; 51 import jdk.jfr.internal.SecuritySupport.SafePath; 52 import jdk.jfr.internal.Utils; 53 import jdk.jfr.internal.WriteableUserPath; 54 import jdk.jfr.internal.consumer.EventDirectoryStream; 55 import jdk.jfr.internal.consumer.FileAccess; 56 import jdk.jfr.internal.instrument.JDKEvents; 57 58 /** 59 * The management API in module jdk.management.jfr should be built on top of the 60 * public API in jdk.jfr. Before putting more functionality here, consider if it 61 * should not be part of the public API, and if not, please provide motivation 62 * 63 */ 64 public final class ManagementSupport { 65 66 // Purpose of this method is to expose the event types to the 67 // FlightRecorderMXBean without instantiating Flight Recorder. 68 // 69 // This allows: 70 // 71 // 1) discoverability, so event settings can be exposed without the need to 72 // create a new Recording in FlightRecorderMXBean. 73 // 74 // 2) a graphical JMX client to list all attributes to the user, without 75 // loading JFR memory buffers. This is especially important when there is 76 // no intent to use Flight Recorder. 77 // 78 // An alternative design would be to make FlightRecorder#getEventTypes 79 // static, but it would the make the API look strange 80 // getEventTypes()81 public static List<EventType> getEventTypes() { 82 // would normally be checked when a Flight Recorder instance is created 83 Utils.checkAccessFlightRecorder(); 84 if (JVMSupport.isNotAvailable()) { 85 return List.of(); 86 } 87 JDKEvents.initialize(); // make sure JDK events are available 88 return Collections.unmodifiableList(MetadataRepository.getInstance().getRegisteredEventTypes()); 89 } 90 91 // Reuse internal code for parsing a timespan parseTimespan(String s)92 public static long parseTimespan(String s) { 93 return Utils.parseTimespan(s); 94 } 95 96 // Reuse internal code for converting nanoseconds since epoch to Instant epochNanosToInstant(long epochNanos)97 public static Instant epochNanosToInstant(long epochNanos) { 98 return Utils.epochNanosToInstant(epochNanos); 99 } 100 101 // Reuse internal code for formatting settings formatTimespan(Duration dValue, String separation)102 public static final String formatTimespan(Duration dValue, String separation) { 103 return Utils.formatTimespan(dValue, separation); 104 } 105 106 // Reuse internal logging mechanism logError(String message)107 public static void logError(String message) { 108 Logger.log(LogTag.JFR, LogLevel.ERROR, message); 109 } 110 111 // Reuse internal logging mechanism logDebug(String message)112 public static void logDebug(String message) { 113 Logger.log(LogTag.JFR, LogLevel.DEBUG, message); 114 } 115 116 // Get the textual representation when the destination was set, which 117 // requires access to jdk.jfr.internal.PlatformRecording getDestinationOriginalText(Recording recording)118 public static String getDestinationOriginalText(Recording recording) { 119 PlatformRecording pr = PrivateAccess.getInstance().getPlatformRecording(recording); 120 WriteableUserPath wup = pr.getDestination(); 121 return wup == null ? null : wup.getOriginalText(); 122 } 123 124 // Needed to check if destination can be set, so FlightRecorderMXBean::setRecordingOption 125 // can abort if not all data is valid checkSetDestination(Recording recording, String destination)126 public static void checkSetDestination(Recording recording, String destination) throws IOException{ 127 PlatformRecording pr = PrivateAccess.getInstance().getPlatformRecording(recording); 128 if(destination != null){ 129 WriteableUserPath wup = new WriteableUserPath(Paths.get(destination)); 130 pr.checkSetDestination(wup); 131 } 132 } 133 134 // Needed to modify setting using fluent API. newEventSettings(EventSettingsModifier esm)135 public static EventSettings newEventSettings(EventSettingsModifier esm) { 136 return PrivateAccess.getInstance().newEventSettings(esm); 137 } 138 139 // When streaming an ongoing recording, consumed chunks should be removed removeBefore(Recording recording, Instant timestamp)140 public static void removeBefore(Recording recording, Instant timestamp) { 141 PlatformRecording pr = PrivateAccess.getInstance().getPlatformRecording(recording); 142 pr.removeBefore(timestamp); 143 } 144 145 // Needed callback to detect when a chunk has been parsed. removePath(Recording recording, Path path)146 public static void removePath(Recording recording, Path path) { 147 PlatformRecording pr = PrivateAccess.getInstance().getPlatformRecording(recording); 148 pr.removePath(new SafePath(path)); 149 } 150 151 // Needed callback to detect when a chunk has been parsed. setOnChunkCompleteHandler(EventStream stream, Consumer<Long> consumer)152 public static void setOnChunkCompleteHandler(EventStream stream, Consumer<Long> consumer) { 153 EventDirectoryStream eds = (EventDirectoryStream) stream; 154 eds.setChunkCompleteHandler(consumer); 155 } 156 157 // Needed to start an ongoing stream at the right chunk, which 158 // can be identified by the start time with nanosecond precision. getStartTimeNanos(Recording recording)159 public static long getStartTimeNanos(Recording recording) { 160 PlatformRecording pr = PrivateAccess.getInstance().getPlatformRecording(recording); 161 return pr.getStartNanos(); 162 } 163 164 // Needed to produce Configuration objects for MetadataEvent newConfiguration(String name, String label, String description, String provider, Map<String, String> settings, String contents)165 public static Configuration newConfiguration(String name, String label, String description, String provider, 166 Map<String, String> settings, String contents) { 167 return PrivateAccess.getInstance().newConfiguration(name, label, description, provider, settings, contents); 168 } 169 170 // Can't use EventStream.openRepository(...) because 171 // EventStream::onMetadataData need to supply MetadataEvent 172 // with configuration objects newEventDirectoryStream( @uppressWarningsR) AccessControlContext acc, Path directory, List<Configuration> confs)173 public static EventStream newEventDirectoryStream( 174 @SuppressWarnings("removal") 175 AccessControlContext acc, 176 Path directory, 177 List<Configuration> confs) throws IOException { 178 return new EventDirectoryStream( 179 acc, 180 directory, 181 FileAccess.UNPRIVILEGED, 182 null, 183 confs, 184 false 185 ); 186 } 187 } 188