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