1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.tools.metalava; 18 19 import java.lang.reflect.Method; 20 import java.util.List; 21 22 /** 23 * A collection of wrapper functions for java.lang.management APIs. Unfortunately we need to 24 * use reflections for them due to b/151224432. 25 * 26 * TODO(b/151224432) Resolve the device-side dependency and stop using reflections here. 27 * 28 * To avoid extra possible allocations around casts in kotlin, it's written in Java. 29 */ 30 final class ManagementWrapper { 31 private static Class<?>[] EMPTY_CLASSES = new Class[]{}; 32 private static Object[] EMPTY_ARGS = new Object[]{}; 33 onReflectionException(Exception e)34 private static void onReflectionException(Exception e) { 35 throw new RuntimeException(e); 36 } 37 38 private abstract static class Lazy<T> { 39 private boolean mInitialized; 40 private T mValue; 41 init()42 abstract T init(); 43 get()44 public T get() { 45 if (!mInitialized) { 46 mValue = init(); 47 mInitialized = true; 48 } 49 return mValue; 50 } 51 } 52 53 private static class LazyClass extends Lazy<Class<?>> { 54 private final String mName; 55 LazyClass(String name)56 private LazyClass(String name) { 57 mName = name; 58 } 59 60 @Override init()61 Class<?> init() { 62 try { 63 return Class.forName(mName); 64 } catch (Exception e) { 65 onReflectionException(e); 66 return null; 67 } 68 } 69 } 70 71 private static class LazyMethod<TV> extends Lazy<Method> { 72 private final LazyClass mClazz; 73 private final String mName; 74 private TV mCachedInvokeResult; 75 private TV mDefault; 76 LazyMethod(LazyClass clazz, String name)77 private LazyMethod(LazyClass clazz, String name) { 78 this(clazz, name, null); 79 } 80 LazyMethod(LazyClass clazz, String name, TV defaultValue)81 private LazyMethod(LazyClass clazz, String name, TV defaultValue) { 82 mClazz = clazz; 83 mName = name; 84 mDefault = defaultValue; 85 } 86 87 @Override init()88 Method init() { 89 try { 90 final Class<?> clazz = mClazz.get(); 91 final Method m = clazz.getMethod(mName, EMPTY_CLASSES); 92 m.setAccessible(true); 93 return m; 94 } catch (Exception e) { 95 onReflectionException(e); 96 return null; 97 } 98 } 99 100 @SuppressWarnings("unchecked") invoke(Object target)101 public TV invoke(Object target) { 102 try { 103 return (TV) get().invoke(target, EMPTY_ARGS); 104 } catch (Exception e) { 105 onReflectionException(e); 106 return mDefault; 107 } 108 } 109 invokeOnce(Object target)110 public TV invokeOnce(Object target) { 111 if (mCachedInvokeResult == null) { 112 mCachedInvokeResult = invoke(target); 113 } 114 return mCachedInvokeResult; 115 } 116 } 117 118 private static LazyClass sFactoryClazz = new LazyClass("java.lang.management.ManagementFactory"); 119 private static LazyClass sThreadMxClazz = new LazyClass("java.lang.management.ThreadMXBean"); 120 private static LazyClass sRuntimeMxClazz = new LazyClass("java.lang.management.RuntimeMXBean"); 121 private static LazyClass sMemoryMxClazz = new LazyClass("java.lang.management.MemoryMXBean"); 122 private static LazyClass sMemoryUsageClazz = new LazyClass("java.lang.management.MemoryUsage"); 123 124 private static LazyMethod<Object> sRuntimeMxGetter = new LazyMethod<>(sFactoryClazz, "getRuntimeMXBean"); 125 private static LazyMethod<Object> sThreadMxGetter = new LazyMethod<>(sFactoryClazz, "getThreadMXBean"); 126 private static LazyMethod<Object> sMemoryMxGetter = new LazyMethod<>(sFactoryClazz, "getMemoryMXBean"); 127 128 // RuntimeMxBean functions 129 private static LazyMethod<List<String>> sGetInputArguments = new LazyMethod<>(sRuntimeMxClazz, "getInputArguments"); 130 131 /** This corresponds to RuntimeMxBean.getInputArguments() */ getVmArguments()132 public static List<String> getVmArguments() { 133 return sGetInputArguments.invoke(sRuntimeMxGetter.invokeOnce(null)); 134 } 135 136 // ThreadMxBean functions 137 private static LazyMethod<Long> sUserTime = new LazyMethod<>(sThreadMxClazz, "getCurrentThreadUserTime", 0L); 138 private static LazyMethod<Long> sCpuTime = new LazyMethod<>(sThreadMxClazz, "getCurrentThreadCpuTime", 0L); 139 140 /** This corresponds to ThreadMxBean.getCurrentThreadUserTime() */ getUserTimeNano()141 public static long getUserTimeNano() { 142 return sUserTime.invoke(sThreadMxGetter.invokeOnce(null)); 143 } 144 145 /** This corresponds to ThreadMxBean.getCurrentThreadCpuTime() */ getCpuTimeNano()146 public static long getCpuTimeNano() { 147 return sCpuTime.invoke(sThreadMxGetter.invokeOnce(null)); 148 } 149 150 // MemoryMxBean functions 151 152 /** Corresponds to java.lang.management.MemoryUsage */ 153 public static final class MemoryUsage { 154 public final long init; 155 public final long used; 156 public final long committed; 157 public final long max; 158 MemoryUsage(long init, long used, long committed, long max)159 public MemoryUsage(long init, long used, long committed, long max) { 160 this.init = init; 161 this.used = used; 162 this.committed = committed; 163 this.max = max; 164 } 165 } 166 167 // This returns a java.lang.management.MemoryUsage instance. 168 private static LazyMethod<Object> sGetHeapMemoryUsage = new LazyMethod<>(sMemoryMxClazz, "getHeapMemoryUsage"); 169 private static LazyMethod<Long> sGetInitMemory = new LazyMethod<>(sMemoryUsageClazz, "getInit", 0L); 170 private static LazyMethod<Long> sGetUsedMemory = new LazyMethod<>(sMemoryUsageClazz, "getUsed", 0L); 171 private static LazyMethod<Long> sGetCommittedMemory = new LazyMethod<>(sMemoryUsageClazz, "getCommitted", 0L); 172 private static LazyMethod<Long> sGetMaxMemory = new LazyMethod<>(sMemoryUsageClazz, "getMax", 0L); 173 174 /** Corresponds to java.lang.management.MemoryUsage.getHeapMemoryUsage() */ getHeapUsage()175 public static MemoryUsage getHeapUsage() { 176 final Object heapUsage = sGetHeapMemoryUsage.invoke(sMemoryMxGetter.invokeOnce(null)); 177 178 return new MemoryUsage( 179 sGetInitMemory.invoke(heapUsage), 180 sGetUsedMemory.invoke(heapUsage), 181 sGetCommittedMemory.invoke(heapUsage), 182 sGetMaxMemory.invoke(heapUsage) 183 ); 184 } 185 } 186