1 /*
2  * Copyright (c) 2003, 2018, 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         }
349         return bufferPools;
350     }
351 
352     private final static String BUFFER_POOL_MXBEAN_NAME = "java.nio:type=BufferPool";
353 
354     /**
355      * Creates management interface for the given buffer pool.
356      */
357     private static BufferPoolMXBean
358         createBufferPoolMXBean(final JavaNioAccess.BufferPool pool)
359     {
360         return new BufferPoolMXBean() {
361             private volatile ObjectName objname;  // created lazily
362             @Override
363             public ObjectName getObjectName() {
364                 ObjectName result = objname;
365                 if (result == null) {
366                     synchronized (this) {
367                         result = objname;
368                         if (result == null) {
369                             result = Util.newObjectName(BUFFER_POOL_MXBEAN_NAME +
370                                 ",name=" + pool.getName());
371                             objname = result;
372                         }
373                     }
374                 }
375                 return result;
376             }
377             @Override
378             public String getName() {
379                 return pool.getName();
380             }
381             @Override
382             public long getCount() {
383                 return pool.getCount();
384             }
385             @Override
386             public long getTotalCapacity() {
387                 return pool.getTotalCapacity();
388             }
389             @Override
390             public long getMemoryUsed() {
391                 return pool.getMemoryUsed();
392             }
393         };
394     }
395 
396     private static HotspotRuntime hsRuntimeMBean = null;
397     private static HotspotClassLoading hsClassMBean = null;
398     private static HotspotThread hsThreadMBean = null;
399     private static HotspotCompilation hsCompileMBean = null;
400     private static HotspotMemory hsMemoryMBean = null;
401 
402     /**
403      * This method is for testing only.
404      */
405     public static synchronized HotspotRuntimeMBean getHotspotRuntimeMBean() {
406         if (hsRuntimeMBean == null) {
407             hsRuntimeMBean = new HotspotRuntime(jvm);
408         }
409         return hsRuntimeMBean;
410     }
411 
412     /**
413      * This method is for testing only.
414      */
415     public static synchronized HotspotClassLoadingMBean getHotspotClassLoadingMBean() {
416         if (hsClassMBean == null) {
417             hsClassMBean = new HotspotClassLoading(jvm);
418         }
419         return hsClassMBean;
420     }
421 
422     /**
423      * This method is for testing only.
424      */
425     public static synchronized HotspotThreadMBean getHotspotThreadMBean() {
426         if (hsThreadMBean == null) {
427             hsThreadMBean = new HotspotThread(jvm);
428         }
429         return hsThreadMBean;
430     }
431 
432     /**
433      * This method is for testing only.
434      */
435     public static synchronized HotspotMemoryMBean getHotspotMemoryMBean() {
436         if (hsMemoryMBean == null) {
437             hsMemoryMBean = new HotspotMemory(jvm);
438         }
439         return hsMemoryMBean;
440     }
441 
442     /**
443      * This method is for testing only.
444      */
445     public static synchronized HotspotCompilationMBean getHotspotCompilationMBean() {
446         if (hsCompileMBean == null) {
447             hsCompileMBean = new HotspotCompilation(jvm);
448         }
449         return hsCompileMBean;
450     }
451 
452     /**
453      * Registers a given MBean if not registered in the MBeanServer;
454      * otherwise, just return.
455      */
456     private static void addMBean(MBeanServer mbs, Object mbean, String mbeanName) {
457         try {
458             final ObjectName objName = Util.newObjectName(mbeanName);
459 
460             // inner class requires these fields to be final
461             final MBeanServer mbs0 = mbs;
462             final Object mbean0 = mbean;
463             AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
464                 public Void run() throws MBeanRegistrationException,
465                                          NotCompliantMBeanException {
466                     try {
467                         mbs0.registerMBean(mbean0, objName);
468                         return null;
469                     } catch (InstanceAlreadyExistsException e) {
470                         // if an instance with the object name exists in
471                         // the MBeanServer ignore the exception
472                     }
473                     return null;
474                 }
475             });
476         } catch (PrivilegedActionException e) {
477             throw Util.newException(e.getException());
478         }
479     }
480 
481     private final static String HOTSPOT_CLASS_LOADING_MBEAN_NAME =
482         "sun.management:type=HotspotClassLoading";
483 
484     private final static String HOTSPOT_COMPILATION_MBEAN_NAME =
485         "sun.management:type=HotspotCompilation";
486 
487     private final static String HOTSPOT_MEMORY_MBEAN_NAME =
488         "sun.management:type=HotspotMemory";
489 
490     private static final String HOTSPOT_RUNTIME_MBEAN_NAME =
491         "sun.management:type=HotspotRuntime";
492 
493     private final static String HOTSPOT_THREAD_MBEAN_NAME =
494         "sun.management:type=HotspotThreading";
495 
496     static void registerInternalMBeans(MBeanServer mbs) {
497         // register all internal MBeans if not registered
498         // No exception is thrown if a MBean with that object name
499         // already registered
500         addMBean(mbs, getHotspotClassLoadingMBean(),
501             HOTSPOT_CLASS_LOADING_MBEAN_NAME);
502         addMBean(mbs, getHotspotMemoryMBean(),
503             HOTSPOT_MEMORY_MBEAN_NAME);
504         addMBean(mbs, getHotspotRuntimeMBean(),
505             HOTSPOT_RUNTIME_MBEAN_NAME);
506         addMBean(mbs, getHotspotThreadMBean(),
507             HOTSPOT_THREAD_MBEAN_NAME);
508 
509         // CompilationMBean may not exist
510         if (getCompilationMXBean() != null) {
511             addMBean(mbs, getHotspotCompilationMBean(),
512                 HOTSPOT_COMPILATION_MBEAN_NAME);
513         }
514     }
515 
516     private static void unregisterMBean(MBeanServer mbs, String mbeanName) {
517         try {
518             final ObjectName objName = Util.newObjectName(mbeanName);
519 
520             // inner class requires these fields to be final
521             final MBeanServer mbs0 = mbs;
522             AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
523                 public Void run() throws MBeanRegistrationException,
524                                            RuntimeOperationsException  {
525                     try {
526                         mbs0.unregisterMBean(objName);
527                     } catch (InstanceNotFoundException e) {
528                         // ignore exception if not found
529                     }
530                     return null;
531                 }
532             });
533         } catch (PrivilegedActionException e) {
534             throw Util.newException(e.getException());
535         }
536     }
537 
538     static void unregisterInternalMBeans(MBeanServer mbs) {
539         // unregister all internal MBeans
540         unregisterMBean(mbs, HOTSPOT_CLASS_LOADING_MBEAN_NAME);
541         unregisterMBean(mbs, HOTSPOT_MEMORY_MBEAN_NAME);
542         unregisterMBean(mbs, HOTSPOT_RUNTIME_MBEAN_NAME);
543         unregisterMBean(mbs, HOTSPOT_THREAD_MBEAN_NAME);
544 
545         // CompilationMBean may not exist
546         if (getCompilationMXBean() != null) {
547             unregisterMBean(mbs, HOTSPOT_COMPILATION_MBEAN_NAME);
548         }
549     }
550 
551     public static boolean isThreadSuspended(int state) {
552         return ((state & JMM_THREAD_STATE_FLAG_SUSPENDED) != 0);
553     }
554 
555     public static boolean isThreadRunningNative(int state) {
556         return ((state & JMM_THREAD_STATE_FLAG_NATIVE) != 0);
557     }
558 
559     public static Thread.State toThreadState(int state) {
560         // suspended and native bits may be set in state
561         int threadStatus = state & ~JMM_THREAD_STATE_FLAG_MASK;
562         return jdk.internal.misc.VM.toThreadState(threadStatus);
563     }
564 
565     // These values are defined in jmm.h
566     private static final int JMM_THREAD_STATE_FLAG_MASK = 0xFFF00000;
567     private static final int JMM_THREAD_STATE_FLAG_SUSPENDED = 0x00100000;
568     private static final int JMM_THREAD_STATE_FLAG_NATIVE = 0x00400000;
569 
570     // Invoked by the VM
571     private static MemoryPoolMXBean createMemoryPool
572         (String name, boolean isHeap, long uThreshold, long gcThreshold) {
573         return new MemoryPoolImpl(name, isHeap, uThreshold, gcThreshold);
574     }
575 
576     private static MemoryManagerMXBean createMemoryManager(String name) {
577         return new MemoryManagerImpl(name);
578     }
579 
580     private static GarbageCollectorMXBean
581         createGarbageCollector(String name, String type) {
582 
583         // ignore type parameter which is for future extension
584         return new GarbageCollectorImpl(name);
585     }
586 }
587