1 /*
  2  * Copyright (c) 2016, 2023, 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.instrument;
 27 
 28 import java.util.ArrayList;
 29 import java.util.List;
 30 import java.util.Properties;
 31 
 32 import jdk.internal.access.SharedSecrets;
 33 import jdk.internal.event.ThrowableTracer;
 34 import jdk.jfr.Event;
 35 import jdk.jfr.events.ActiveRecordingEvent;
 36 import jdk.jfr.events.ActiveSettingEvent;
 37 import jdk.jfr.events.ContainerIOUsageEvent;
 38 import jdk.jfr.events.ContainerConfigurationEvent;
 39 import jdk.jfr.events.ContainerCPUUsageEvent;
 40 import jdk.jfr.events.ContainerCPUThrottlingEvent;
 41 import jdk.jfr.events.ContainerMemoryUsageEvent;
 42 import jdk.jfr.events.DirectBufferStatisticsEvent;
 43 import jdk.jfr.events.FileForceEvent;
 44 import jdk.jfr.events.FileReadEvent;
 45 import jdk.jfr.events.FileWriteEvent;
 46 import jdk.jfr.events.InitialSecurityPropertyEvent;
 47 
 48 import jdk.jfr.internal.JVM;
 49 import jdk.jfr.internal.LogLevel;
 50 import jdk.jfr.internal.LogTag;
 51 import jdk.jfr.internal.Logger;
 52 import jdk.jfr.internal.SecuritySupport;
 53 import jdk.jfr.internal.periodic.PeriodicEvents;
 54 import jdk.internal.platform.Container;
 55 import jdk.internal.platform.Metrics;
 56 
 57 public final class JDKEvents {
 58 
 59     private static final Class<?>[] eventClasses = {
 60         FileForceEvent.class,
 61         FileReadEvent.class,
 62         FileWriteEvent.class,
 63         ActiveSettingEvent.class,
 64         ActiveRecordingEvent.class,
 65         // jdk.internal.event.* classes need their mirror
 66         // event class to be listed in the MirrorEvents class.
 67         jdk.internal.event.DeserializationEvent.class,
 68         jdk.internal.event.ErrorThrownEvent.class,
 69         jdk.internal.event.ExceptionStatisticsEvent.class,
 70         jdk.internal.event.ExceptionThrownEvent.class,
 71         jdk.internal.event.ProcessStartEvent.class,
 72         jdk.internal.event.SecurityPropertyModificationEvent.class,
 73         jdk.internal.event.SecurityProviderServiceEvent.class,
 74         jdk.internal.event.SerializationMisdeclarationEvent.class,
 75         jdk.internal.event.SocketReadEvent.class,
 76         jdk.internal.event.SocketWriteEvent.class,
 77         jdk.internal.event.ThreadSleepEvent.class,
 78         jdk.internal.event.TLSHandshakeEvent.class,
 79         jdk.internal.event.VirtualThreadStartEvent.class,
 80         jdk.internal.event.VirtualThreadEndEvent.class,
 81         jdk.internal.event.VirtualThreadSubmitFailedEvent.class,
 82         jdk.internal.event.X509CertificateEvent.class,
 83         jdk.internal.event.X509ValidationEvent.class,
 84         DirectBufferStatisticsEvent.class,
 85         InitialSecurityPropertyEvent.class,
 86     };
 87 
 88     // This is a list of the classes with instrumentation code that should be applied.
 89     private static final Class<?>[] instrumentationClasses = new Class<?>[] {
 90         FileInputStreamInstrumentor.class,
 91         FileOutputStreamInstrumentor.class,
 92         RandomAccessFileInstrumentor.class,
 93         FileChannelImplInstrumentor.class
 94     };
 95 
 96     private static final Class<?>[] targetClasses = new Class<?>[instrumentationClasses.length];
 97     private static final Runnable emitExceptionStatistics = JDKEvents::emitExceptionStatistics;
 98     private static final Runnable emitDirectBufferStatistics = JDKEvents::emitDirectBufferStatistics;
 99     private static final Runnable emitContainerConfiguration = JDKEvents::emitContainerConfiguration;
100     private static final Runnable emitContainerCPUUsage = JDKEvents::emitContainerCPUUsage;
101     private static final Runnable emitContainerCPUThrottling = JDKEvents::emitContainerCPUThrottling;
102     private static final Runnable emitContainerMemoryUsage = JDKEvents::emitContainerMemoryUsage;
103     private static final Runnable emitContainerIOUsage = JDKEvents::emitContainerIOUsage;
104     private static final Runnable emitInitialSecurityProperties = JDKEvents::emitInitialSecurityProperties;
105     private static Metrics containerMetrics = null;
106     private static boolean initializationTriggered;
107 
108     @SuppressWarnings("unchecked")
109     public static synchronized void initialize() {
110         try {
111             if (initializationTriggered == false) {
112                 for (Class<?> eventClass : eventClasses) {
113                     SecuritySupport.registerEvent((Class<? extends Event>) eventClass);
114                 }
115                 PeriodicEvents.addJDKEvent(jdk.internal.event.ExceptionStatisticsEvent.class, emitExceptionStatistics);
116                 PeriodicEvents.addJDKEvent(DirectBufferStatisticsEvent.class, emitDirectBufferStatistics);
117                 PeriodicEvents.addJDKEvent(InitialSecurityPropertyEvent.class, emitInitialSecurityProperties);
118 
119                 initializeContainerEvents();
120                 ThrowableTracer.enable();
121                 initializationTriggered = true;
122             }
123         } catch (Exception e) {
124             Logger.log(LogTag.JFR_SYSTEM, LogLevel.WARN, "Could not initialize JDK events. " + e.getMessage());
125         }
126     }
127 
128     public static void addInstrumentation() {
129         try {
130             List<Class<?>> list = new ArrayList<>();
131             for (int i = 0; i < instrumentationClasses.length; i++) {
132                 JIInstrumentationTarget tgt = instrumentationClasses[i].getAnnotation(JIInstrumentationTarget.class);
133                 Class<?> clazz = Class.forName(tgt.value());
134                 targetClasses[i] = clazz;
135                 list.add(clazz);
136             }
137             Logger.log(LogTag.JFR_SYSTEM, LogLevel.INFO, "Retransformed JDK classes");
138             JVM.retransformClasses(list.toArray(new Class<?>[list.size()]));
139         } catch (IllegalStateException ise) {
140             throw ise;
141         } catch (Exception e) {
142             Logger.log(LogTag.JFR_SYSTEM, LogLevel.WARN, "Could not add instrumentation for JDK events. " + e.getMessage());
143         }
144     }
145 
146     private static void initializeContainerEvents() {
147         if (JVM.isContainerized() ) {
148             Logger.log(LogTag.JFR_SYSTEM, LogLevel.DEBUG, "JVM is containerized");
149             containerMetrics = Container.metrics();
150             if (containerMetrics != null) {
151                 Logger.log(LogTag.JFR_SYSTEM, LogLevel.DEBUG, "Container metrics are available");
152             }
153         }
154         // The registration of events and hooks are needed to provide metadata,
155         // even when not running in a container
156         SecuritySupport.registerEvent(ContainerConfigurationEvent.class);
157         SecuritySupport.registerEvent(ContainerCPUUsageEvent.class);
158         SecuritySupport.registerEvent(ContainerCPUThrottlingEvent.class);
159         SecuritySupport.registerEvent(ContainerMemoryUsageEvent.class);
160         SecuritySupport.registerEvent(ContainerIOUsageEvent.class);
161 
162         PeriodicEvents.addJDKEvent(ContainerConfigurationEvent.class, emitContainerConfiguration);
163         PeriodicEvents.addJDKEvent(ContainerCPUUsageEvent.class, emitContainerCPUUsage);
164         PeriodicEvents.addJDKEvent(ContainerCPUThrottlingEvent.class, emitContainerCPUThrottling);
165         PeriodicEvents.addJDKEvent(ContainerMemoryUsageEvent.class, emitContainerMemoryUsage);
166         PeriodicEvents.addJDKEvent(ContainerIOUsageEvent.class, emitContainerIOUsage);
167     }
168 
169     private static void emitExceptionStatistics() {
170         ThrowableTracer.emitStatistics();
171     }
172 
173     private static void emitContainerConfiguration() {
174         if (containerMetrics != null) {
175             ContainerConfigurationEvent t = new ContainerConfigurationEvent();
176             t.containerType = containerMetrics.getProvider();
177             t.cpuSlicePeriod = containerMetrics.getCpuPeriod();
178             t.cpuQuota = containerMetrics.getCpuQuota();
179             t.cpuShares = containerMetrics.getCpuShares();
180             t.effectiveCpuCount = containerMetrics.getEffectiveCpuCount();
181             t.memorySoftLimit = containerMetrics.getMemorySoftLimit();
182             t.memoryLimit = containerMetrics.getMemoryLimit();
183             t.swapMemoryLimit = containerMetrics.getMemoryAndSwapLimit();
184             t.hostTotalMemory = JVM.hostTotalMemory();
185             t.hostTotalSwapMemory = JVM.hostTotalSwapMemory();
186             t.commit();
187         }
188     }
189 
190     private static void emitContainerCPUUsage() {
191         if (containerMetrics != null) {
192             ContainerCPUUsageEvent event = new ContainerCPUUsageEvent();
193 
194             event.cpuTime = containerMetrics.getCpuUsage();
195             event.cpuSystemTime = containerMetrics.getCpuSystemUsage();
196             event.cpuUserTime = containerMetrics.getCpuUserUsage();
197             event.commit();
198         }
199     }
200     private static void emitContainerMemoryUsage() {
201         if (containerMetrics != null) {
202             ContainerMemoryUsageEvent event = new ContainerMemoryUsageEvent();
203 
204             event.memoryFailCount = containerMetrics.getMemoryFailCount();
205             event.memoryUsage = containerMetrics.getMemoryUsage();
206             event.swapMemoryUsage = containerMetrics.getMemoryAndSwapUsage();
207             event.commit();
208         }
209     }
210 
211     private static void emitContainerIOUsage() {
212         if (containerMetrics != null) {
213             ContainerIOUsageEvent event = new ContainerIOUsageEvent();
214 
215             event.serviceRequests = containerMetrics.getBlkIOServiceCount();
216             event.dataTransferred = containerMetrics.getBlkIOServiced();
217             event.commit();
218         }
219     }
220 
221     private static void emitContainerCPUThrottling() {
222         if (containerMetrics != null) {
223             ContainerCPUThrottlingEvent event = new ContainerCPUThrottlingEvent();
224 
225             event.cpuElapsedSlices = containerMetrics.getCpuNumPeriods();
226             event.cpuThrottledSlices = containerMetrics.getCpuNumThrottled();
227             event.cpuThrottledTime = containerMetrics.getCpuThrottledTime();
228             event.commit();
229         }
230     }
231 
232     @SuppressWarnings("deprecation")
233     public static byte[] retransformCallback(Class<?> klass, byte[] oldBytes) throws Throwable {
234         for (int i = 0; i < targetClasses.length; i++) {
235             if (targetClasses[i].equals(klass)) {
236                 Class<?> c = instrumentationClasses[i];
237                 if (Logger.shouldLog(LogTag.JFR_SYSTEM, LogLevel.TRACE)) {
238                     Logger.log(LogTag.JFR_SYSTEM, LogLevel.TRACE, "Processing instrumentation class: " + c);
239                 }
240                 return new JIClassInstrumentation(instrumentationClasses[i], klass, oldBytes).getNewBytes();
241             }
242         }
243         return oldBytes;
244     }
245 
246     public static void remove() {
247         PeriodicEvents.removeEvent(emitExceptionStatistics);
248         PeriodicEvents.removeEvent(emitDirectBufferStatistics);
249         PeriodicEvents.removeEvent(emitInitialSecurityProperties);
250 
251         PeriodicEvents.removeEvent(emitContainerConfiguration);
252         PeriodicEvents.removeEvent(emitContainerCPUUsage);
253         PeriodicEvents.removeEvent(emitContainerCPUThrottling);
254         PeriodicEvents.removeEvent(emitContainerMemoryUsage);
255         PeriodicEvents.removeEvent(emitContainerIOUsage);
256     }
257 
258     private static void emitDirectBufferStatistics() {
259         DirectBufferStatisticsEvent e = new DirectBufferStatisticsEvent();
260         e.commit();
261     }
262 
263     private static void emitInitialSecurityProperties() {
264         Properties p = SharedSecrets.getJavaSecurityPropertiesAccess().getInitialProperties();
265         if (p != null) {
266             for (String key : p.stringPropertyNames()) {
267                 InitialSecurityPropertyEvent e = new InitialSecurityPropertyEvent();
268                 e.key = key;
269                 e.value = p.getProperty(key);
270                 e.commit();
271             }
272         }
273     }
274 }