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