1 /*
2  * Copyright (c) 2003, 2019, 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 sun.management;
27 
28 import java.lang.management.*;
29 import java.lang.reflect.InvocationTargetException;
30 import java.lang.reflect.Method;
31 import javax.management.InstanceAlreadyExistsException;
32 import javax.management.InstanceNotFoundException;
33 import javax.management.MBeanServer;
34 import javax.management.MBeanRegistrationException;
35 import javax.management.NotCompliantMBeanException;
36 import javax.management.ObjectName;
37 import javax.management.RuntimeOperationsException;
38 import java.security.AccessController;
39 import java.security.PrivilegedActionException;
40 import java.security.PrivilegedExceptionAction;
41 
42 import jdk.internal.access.JavaNioAccess;
43 import jdk.internal.access.SharedSecrets;
44 
45 import java.util.ArrayList;
46 import java.util.List;
47 
48 import java.lang.reflect.UndeclaredThrowableException;
49 import java.security.PrivilegedAction;
50 import java.util.Arrays;
51 import java.util.Collections;
52 import java.util.HashMap;
53 import java.util.Map;
54 import java.util.Optional;
55 
56 /**
57  * ManagementFactoryHelper provides static factory methods to create
58  * instances of the management interface.
59  */
60 public class ManagementFactoryHelper {
61     static {
62         // make sure that the management lib is loaded within
63         // java.lang.management.ManagementFactory
64         jdk.internal.misc.Unsafe.getUnsafe().ensureClassInitialized(ManagementFactory.class);
65     }
66 
67     private static final VMManagement jvm = new VMManagementImpl();
68 
ManagementFactoryHelper()69     private ManagementFactoryHelper() {};
70 
getVMManagement()71     public static VMManagement getVMManagement() {
72         return jvm;
73     }
74 
75     static final String LOGGING_MXBEAN_NAME = "java.util.logging:type=Logging";
76     private static ClassLoadingImpl    classMBean = null;
77     private static MemoryImpl          memoryMBean = null;
78     private static ThreadImpl          threadMBean = null;
79     private static RuntimeImpl         runtimeMBean = null;
80     private static CompilationImpl     compileMBean = null;
81     private static BaseOperatingSystemImpl osMBean = null;
82 
getClassLoadingMXBean()83     public static synchronized ClassLoadingMXBean getClassLoadingMXBean() {
84         if (classMBean == null) {
85             classMBean = new ClassLoadingImpl(jvm);
86         }
87         return classMBean;
88     }
89 
getMemoryMXBean()90     public static synchronized MemoryMXBean getMemoryMXBean() {
91         if (memoryMBean == null) {
92             memoryMBean = new MemoryImpl(jvm);
93         }
94         return memoryMBean;
95     }
96 
getThreadMXBean()97     public static synchronized ThreadMXBean getThreadMXBean() {
98         if (threadMBean == null) {
99             threadMBean = new ThreadImpl(jvm);
100         }
101         return threadMBean;
102     }
103 
getRuntimeMXBean()104     public static synchronized RuntimeMXBean getRuntimeMXBean() {
105         if (runtimeMBean == null) {
106             runtimeMBean = new RuntimeImpl(jvm);
107         }
108         return runtimeMBean;
109     }
110 
getCompilationMXBean()111     public static synchronized CompilationMXBean getCompilationMXBean() {
112         if (compileMBean == null && jvm.getCompilerName() != null) {
113             compileMBean = new CompilationImpl(jvm);
114         }
115         return compileMBean;
116     }
117 
getOperatingSystemMXBean()118     public static synchronized OperatingSystemMXBean getOperatingSystemMXBean() {
119         if (osMBean == null) {
120             osMBean = new BaseOperatingSystemImpl(jvm);
121         }
122         return osMBean;
123     }
124 
getMemoryPoolMXBeans()125     public static List<MemoryPoolMXBean> getMemoryPoolMXBeans() {
126         MemoryPoolMXBean[] pools = MemoryImpl.getMemoryPools();
127         List<MemoryPoolMXBean> list = new ArrayList<>(pools.length);
128         for (MemoryPoolMXBean p : pools) {
129             list.add(p);
130         }
131         return list;
132     }
133 
getMemoryManagerMXBeans()134     public static List<MemoryManagerMXBean> getMemoryManagerMXBeans() {
135         MemoryManagerMXBean[]  mgrs = MemoryImpl.getMemoryManagers();
136         List<MemoryManagerMXBean> result = new ArrayList<>(mgrs.length);
137         for (MemoryManagerMXBean m : mgrs) {
138             result.add(m);
139         }
140         return result;
141     }
142 
getGarbageCollectorMXBeans()143      public static List<GarbageCollectorMXBean> getGarbageCollectorMXBeans() {
144         MemoryManagerMXBean[]  mgrs = MemoryImpl.getMemoryManagers();
145         List<GarbageCollectorMXBean> result = new ArrayList<>(mgrs.length);
146         for (MemoryManagerMXBean m : mgrs) {
147             if (GarbageCollectorMXBean.class.isInstance(m)) {
148                  result.add(GarbageCollectorMXBean.class.cast(m));
149             }
150         }
151         return result;
152     }
153 
getPlatformLoggingMXBean()154     public static PlatformLoggingMXBean getPlatformLoggingMXBean() {
155         if (LoggingMXBeanAccess.isAvailable()) {
156             return PlatformLoggingImpl.MBEAN;
157         } else {
158             return null;
159         }
160     }
161 
isPlatformLoggingMXBeanAvailable()162     public static boolean isPlatformLoggingMXBeanAvailable() {
163         return LoggingMXBeanAccess.isAvailable();
164     }
165 
166     /**
167      * Returns an array of the name of all memory pools.  The order of the memory pools is
168      * significant and maintained in the VM.
169      */
getAllMemoryPoolNames()170     public static String[] getAllMemoryPoolNames() {
171         return Arrays.stream(MemoryImpl.getMemoryPools())
172                 .map(MemoryPoolMXBean::getName)
173                 .toArray(String[]::new);
174     }
175 
176     // The LoggingMXBeanAccess class uses reflection to determine
177     // whether java.util.logging is present, and load the actual LoggingMXBean
178     // implementation.
179     //
180     static final class LoggingMXBeanAccess {
181 
182         final static String LOG_MANAGER_CLASS_NAME = "java.util.logging.LogManager";
183         final static String LOGGING_MXBEAN_CLASS_NAME = "java.util.logging.LoggingMXBean";
184         final static Class<?> LOG_MANAGER_CLASS = loadLoggingClass(LOG_MANAGER_CLASS_NAME);
185 
isAvailable()186         static boolean isAvailable() {
187             return LOG_MANAGER_CLASS != null;
188         }
189 
loadLoggingClass(String className)190         private static Class<?> loadLoggingClass(String className) {
191             return AccessController.doPrivileged(new PrivilegedAction<>() {
192                 @Override
193                 public Class<?> run() {
194                     Optional<Module> logging = ModuleLayer.boot().findModule("java.logging");
195                     if (logging.isPresent()) {
196                         return Class.forName(logging.get(), className);
197                     }
198                     return null;
199                 }
200             });
201         }
202 
initMethodMap(Object impl)203         private Map<String, Method> initMethodMap(Object impl) {
204             if (impl == null) {
205                 return Collections.emptyMap();
206             }
207             Class<?> intfClass = loadLoggingClass(LOGGING_MXBEAN_CLASS_NAME);
208             final Map<String, Method> methodsMap = new HashMap<>();
209             for (Method m : intfClass.getMethods()) {
210                 try {
211                     // Sanity checking: all public methods present in
212                     // java.util.logging.LoggingMXBean should
213                     // also be in PlatformLoggingMXBean
214                     Method specMethod = PlatformLoggingMXBean.class
215                              .getMethod(m.getName(), m.getParameterTypes());
216                     if (specMethod.getReturnType().isAssignableFrom(m.getReturnType())) {
217                         if (methodsMap.putIfAbsent(m.getName(), m) != null) {
218                             throw new RuntimeException("unexpected polymorphic method: "
219                                      + m.getName());
220                         }
221                     }
222                 } catch (NoSuchMethodException x) {
223                     // All methods in java.util.logging.LoggingMXBean should
224                     // also be in PlatformLoggingMXBean
225                     throw new InternalError(x);
226                 }
227             }
228             return Collections.unmodifiableMap(methodsMap);
229         }
230 
getMXBeanImplementation()231         private static Object getMXBeanImplementation() {
232             if (!isAvailable()) {
233                 // should not happen
234                 throw new NoClassDefFoundError(LOG_MANAGER_CLASS_NAME);
235             }
236             try {
237                 final Method m = LOG_MANAGER_CLASS.getMethod("getLoggingMXBean");
238                 return m.invoke(null);
239             } catch (NoSuchMethodException
240                     | IllegalAccessException
241                     | InvocationTargetException x) {
242                 throw new ExceptionInInitializerError(x);
243             }
244          }
245 
246         // The implementation object, which will be invoked through
247         // reflection. The implementation does not need to implement
248         // PlatformLoggingMXBean, but must declare the same methods
249         // with same signatures, and they must be public, with one
250         // exception:
251         // getObjectName will not be called on the implementation object,
252         // so the implementation object does not need to declare such
253         // a method.
254         final Object impl = getMXBeanImplementation();
255         final Map<String, Method> methods = initMethodMap(impl);
256 
LoggingMXBeanAccess()257         LoggingMXBeanAccess() {
258         }
259 
invoke(String methodName, Object... args)260         <T> T invoke(String methodName, Object... args) {
261             Method m = methods.get(methodName);
262             if (m == null) {
263                 throw new UnsupportedOperationException(methodName);
264             }
265             try {
266                 @SuppressWarnings("unchecked")
267                 T result = (T) m.invoke(impl, args);
268                 return result;
269             } catch (IllegalAccessException ex) {
270                 throw new UnsupportedOperationException(ex);
271             } catch (InvocationTargetException ex) {
272                 throw unwrap(ex);
273             }
274         }
275 
unwrap(InvocationTargetException x)276         private static RuntimeException unwrap(InvocationTargetException x) {
277             Throwable t = x.getCause();
278             if (t instanceof RuntimeException) {
279                 return (RuntimeException)t;
280             }
281             if (t instanceof Error) {
282                 throw (Error)t;
283             }
284             return new UndeclaredThrowableException(t == null ? x : t);
285         }
286 
287 
288     }
289 
290     static final class PlatformLoggingImpl implements PlatformLoggingMXBean {
291 
292         private final LoggingMXBeanAccess loggingAccess;
293         private PlatformLoggingImpl(LoggingMXBeanAccess loggingAccess) {
294             this.loggingAccess = loggingAccess;
295         }
296 
297         private volatile ObjectName objname;  // created lazily
298         @Override
299         public ObjectName getObjectName() {
300             ObjectName result = objname;
301             if (result == null) {
302                 synchronized (this) {
303                     result = objname;
304                     if (result == null) {
305                         result = Util.newObjectName(LOGGING_MXBEAN_NAME);
306                         objname = result;
307                     }
308                 }
309             }
310             return result;
311         }
312 
313         @Override
314         public java.util.List<String> getLoggerNames() {
315             return loggingAccess.invoke("getLoggerNames");
316         }
317 
318         @Override
319         public String getLoggerLevel(String loggerName) {
320             return loggingAccess.invoke("getLoggerLevel", loggerName);
321         }
322 
323         @Override
324         public void setLoggerLevel(String loggerName, String levelName) {
325             loggingAccess.invoke("setLoggerLevel", loggerName, levelName);
326         }
327 
328         @Override
329         public String getParentLoggerName(String loggerName) {
330             return loggingAccess.invoke("getParentLoggerName", loggerName);
331         }
332 
333         private static PlatformLoggingImpl getInstance() {
334             return new PlatformLoggingImpl(new LoggingMXBeanAccess());
335          }
336 
337         static final PlatformLoggingMXBean MBEAN = getInstance();
338     }
339 
340     private static List<BufferPoolMXBean> bufferPools = null;
341     public static synchronized List<BufferPoolMXBean> getBufferPoolMXBeans() {
342         if (bufferPools == null) {
343             bufferPools = new ArrayList<>(2);
344             bufferPools.add(createBufferPoolMXBean(SharedSecrets.getJavaNioAccess()
345                 .getDirectBufferPool()));
346             bufferPools.add(createBufferPoolMXBean(sun.nio.ch.FileChannelImpl
347                 .getMappedBufferPool()));
348             bufferPools.add(createBufferPoolMXBean(sun.nio.ch.FileChannelImpl
349                 .getSyncMappedBufferPool()));
350         }
351         return bufferPools;
352     }
353 
354     private final static String BUFFER_POOL_MXBEAN_NAME = "java.nio:type=BufferPool";
355 
356     /**
357      * Creates management interface for the given buffer pool.
358      */
359     private static BufferPoolMXBean
360         createBufferPoolMXBean(final JavaNioAccess.BufferPool pool)
361     {
362         return new BufferPoolMXBean() {
363             private volatile ObjectName objname;  // created lazily
364             @Override
365             public ObjectName getObjectName() {
366                 ObjectName result = objname;
367                 if (result == null) {
368                     synchronized (this) {
369                         result = objname;
370                         if (result == null) {
371                             result = Util.newObjectName(BUFFER_POOL_MXBEAN_NAME +
372                                 ",name=" + pool.getName());
373                             objname = result;
374                         }
375                     }
376                 }
377                 return result;
378             }
379             @Override
380             public String getName() {
381                 return pool.getName();
382             }
383             @Override
384             public long getCount() {
385                 return pool.getCount();
386             }
387             @Override
388             public long getTotalCapacity() {
389                 return pool.getTotalCapacity();
390             }
391             @Override
392             public long getMemoryUsed() {
393                 return pool.getMemoryUsed();
394             }
395         };
396     }
397 
398     private static HotspotRuntime hsRuntimeMBean = null;
399     private static HotspotClassLoading hsClassMBean = null;
400     private static HotspotThread hsThreadMBean = null;
401     private static HotspotCompilation hsCompileMBean = null;
402     private static HotspotMemory hsMemoryMBean = null;
403 
404     /**
405      * This method is for testing only.
406      */
407     public static synchronized HotspotRuntimeMBean getHotspotRuntimeMBean() {
408         if (hsRuntimeMBean == null) {
409             hsRuntimeMBean = new HotspotRuntime(jvm);
410         }
411         return hsRuntimeMBean;
412     }
413 
414     /**
415      * This method is for testing only.
416      */
417     public static synchronized HotspotClassLoadingMBean getHotspotClassLoadingMBean() {
418         if (hsClassMBean == null) {
419             hsClassMBean = new HotspotClassLoading(jvm);
420         }
421         return hsClassMBean;
422     }
423 
424     /**
425      * This method is for testing only.
426      */
427     public static synchronized HotspotThreadMBean getHotspotThreadMBean() {
428         if (hsThreadMBean == null) {
429             hsThreadMBean = new HotspotThread(jvm);
430         }
431         return hsThreadMBean;
432     }
433 
434     /**
435      * This method is for testing only.
436      */
437     public static synchronized HotspotMemoryMBean getHotspotMemoryMBean() {
438         if (hsMemoryMBean == null) {
439             hsMemoryMBean = new HotspotMemory(jvm);
440         }
441         return hsMemoryMBean;
442     }
443 
444     /**
445      * This method is for testing only.
446      */
447     public static synchronized HotspotCompilationMBean getHotspotCompilationMBean() {
448         if (hsCompileMBean == null) {
449             hsCompileMBean = new HotspotCompilation(jvm);
450         }
451         return hsCompileMBean;
452     }
453 
454     /**
455      * Registers a given MBean if not registered in the MBeanServer;
456      * otherwise, just return.
457      */
458     private static void addMBean(MBeanServer mbs, Object mbean, String mbeanName) {
459         try {
460             final ObjectName objName = Util.newObjectName(mbeanName);
461 
462             // inner class requires these fields to be final
463             final MBeanServer mbs0 = mbs;
464             final Object mbean0 = mbean;
465             AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
466                 public Void run() throws MBeanRegistrationException,
467                                          NotCompliantMBeanException {
468                     try {
469                         mbs0.registerMBean(mbean0, objName);
470                         return null;
471                     } catch (InstanceAlreadyExistsException e) {
472                         // if an instance with the object name exists in
473                         // the MBeanServer ignore the exception
474                     }
475                     return null;
476                 }
477             });
478         } catch (PrivilegedActionException e) {
479             throw Util.newException(e.getException());
480         }
481     }
482 
483     private final static String HOTSPOT_CLASS_LOADING_MBEAN_NAME =
484         "sun.management:type=HotspotClassLoading";
485 
486     private final static String HOTSPOT_COMPILATION_MBEAN_NAME =
487         "sun.management:type=HotspotCompilation";
488 
489     private final static String HOTSPOT_MEMORY_MBEAN_NAME =
490         "sun.management:type=HotspotMemory";
491 
492     private static final String HOTSPOT_RUNTIME_MBEAN_NAME =
493         "sun.management:type=HotspotRuntime";
494 
495     private final static String HOTSPOT_THREAD_MBEAN_NAME =
496         "sun.management:type=HotspotThreading";
497 
498     static void registerInternalMBeans(MBeanServer mbs) {
499         // register all internal MBeans if not registered
500         // No exception is thrown if a MBean with that object name
501         // already registered
502         addMBean(mbs, getHotspotClassLoadingMBean(),
503             HOTSPOT_CLASS_LOADING_MBEAN_NAME);
504         addMBean(mbs, getHotspotMemoryMBean(),
505             HOTSPOT_MEMORY_MBEAN_NAME);
506         addMBean(mbs, getHotspotRuntimeMBean(),
507             HOTSPOT_RUNTIME_MBEAN_NAME);
508         addMBean(mbs, getHotspotThreadMBean(),
509             HOTSPOT_THREAD_MBEAN_NAME);
510 
511         // CompilationMBean may not exist
512         if (getCompilationMXBean() != null) {
513             addMBean(mbs, getHotspotCompilationMBean(),
514                 HOTSPOT_COMPILATION_MBEAN_NAME);
515         }
516     }
517 
518     private static void unregisterMBean(MBeanServer mbs, String mbeanName) {
519         try {
520             final ObjectName objName = Util.newObjectName(mbeanName);
521 
522             // inner class requires these fields to be final
523             final MBeanServer mbs0 = mbs;
524             AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
525                 public Void run() throws MBeanRegistrationException,
526                                            RuntimeOperationsException  {
527                     try {
528                         mbs0.unregisterMBean(objName);
529                     } catch (InstanceNotFoundException e) {
530                         // ignore exception if not found
531                     }
532                     return null;
533                 }
534             });
535         } catch (PrivilegedActionException e) {
536             throw Util.newException(e.getException());
537         }
538     }
539 
540     static void unregisterInternalMBeans(MBeanServer mbs) {
541         // unregister all internal MBeans
542         unregisterMBean(mbs, HOTSPOT_CLASS_LOADING_MBEAN_NAME);
543         unregisterMBean(mbs, HOTSPOT_MEMORY_MBEAN_NAME);
544         unregisterMBean(mbs, HOTSPOT_RUNTIME_MBEAN_NAME);
545         unregisterMBean(mbs, HOTSPOT_THREAD_MBEAN_NAME);
546 
547         // CompilationMBean may not exist
548         if (getCompilationMXBean() != null) {
549             unregisterMBean(mbs, HOTSPOT_COMPILATION_MBEAN_NAME);
550         }
551     }
552 
553     public static boolean isThreadSuspended(int state) {
554         return ((state & JMM_THREAD_STATE_FLAG_SUSPENDED) != 0);
555     }
556 
557     public static boolean isThreadRunningNative(int state) {
558         return ((state & JMM_THREAD_STATE_FLAG_NATIVE) != 0);
559     }
560 
561     public static Thread.State toThreadState(int state) {
562         // suspended and native bits may be set in state
563         int threadStatus = state & ~JMM_THREAD_STATE_FLAG_MASK;
564         return jdk.internal.misc.VM.toThreadState(threadStatus);
565     }
566 
567     // These values are defined in jmm.h
568     private static final int JMM_THREAD_STATE_FLAG_MASK = 0xFFF00000;
569     private static final int JMM_THREAD_STATE_FLAG_SUSPENDED = 0x00100000;
570     private static final int JMM_THREAD_STATE_FLAG_NATIVE = 0x00400000;
571 
572     // Invoked by the VM
573     private static MemoryPoolMXBean createMemoryPool
574         (String name, boolean isHeap, long uThreshold, long gcThreshold) {
575         return new MemoryPoolImpl(name, isHeap, uThreshold, gcThreshold);
576     }
577 
578     private static MemoryManagerMXBean createMemoryManager(String name) {
579         return new MemoryManagerImpl(name);
580     }
581 
582     private static GarbageCollectorMXBean
583         createGarbageCollector(String name, String type) {
584 
585         // ignore type parameter which is for future extension
586         return new GarbageCollectorImpl(name);
587     }
588 }
589