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