├── .gitignore ├── Makefile ├── README.md ├── Test.java ├── notes.txt ├── pom.xml └── src ├── main ├── cpp │ ├── native-agent-old.cpp │ └── native-agent.cpp ├── java │ └── com │ │ └── heliosapm │ │ ├── jvmti │ │ ├── agent │ │ │ ├── AbstractVirtualMachineTask.java │ │ │ ├── Agent.java │ │ │ ├── AgentMBean.java │ │ │ ├── EntryComparators.java │ │ │ ├── NativeAgent.java │ │ │ └── VirtualMachineTask.java │ │ ├── extension │ │ │ ├── ExecutionScheduler.java │ │ │ ├── ExecutionSchedulerMXBean.java │ │ │ ├── Scheduled.java │ │ │ ├── ScheduledExtension.java │ │ │ └── impls │ │ │ │ ├── DirectByteBufferAllocations.java │ │ │ │ ├── HotspotExtension.java │ │ │ │ └── thread │ │ │ │ └── ThreadPoolMonitor.java │ │ ├── install │ │ │ ├── AgentInstaller.java │ │ │ ├── AgentOption.java │ │ │ ├── AgentOptionProcessor.java │ │ │ ├── JavaAgent.java │ │ │ ├── JavaAgent2.java │ │ │ └── NativePlatform.java │ │ ├── jmx │ │ │ ├── NativeOps.java │ │ │ └── NativeOpsMXBean.java │ │ ├── metrics │ │ │ └── LongGauge.java │ │ ├── script │ │ │ └── ScriptManager.java │ │ └── util │ │ │ ├── IsolatedClassLoader.java │ │ │ ├── IsolatedClassLoaderMBean.java │ │ │ ├── SystemClock.java │ │ │ └── TimerHistory.java │ │ └── shorthand │ │ └── attach │ │ └── vm │ │ ├── AttachProvider.java │ │ ├── BaseWrappedClass.java │ │ ├── VirtualMachine.java │ │ ├── VirtualMachineBootstrap.java │ │ ├── VirtualMachineDescriptor.java │ │ └── VirtualMachineInvocationException.java └── resources │ ├── defaultconfig.xml │ ├── js │ └── jmx.js │ └── tinylog.properties └── test ├── java └── com │ └── heliosapm │ └── jvmti │ ├── BaseTest.java │ ├── Example.java │ ├── InstancesExample.java │ ├── TopNExample.java │ └── options │ ├── TestAgentOptions.java │ └── TestCommandLine.java └── resources ├── config └── config1.xml └── configs └── config1.xml /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /.settings/ 3 | /.classpath 4 | /.project 5 | hs_err*.log 6 | dependency-reduced-pom.xml 7 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | SHELL = /bin/sh 3 | CC=gcc 4 | LCPP=g++ -v 5 | W64CPP=x86_64-w64-mingw32-g++ 6 | W32CPP=i686-w64-mingw32-g++ 7 | 8 | 9 | C64FLAGS = -O3 -m64 -fPIC -pthread -DLINUX -D_LP64=1 10 | W64FLAGS = -O3 -m64 -pthread -DWINDOWS -D_LP64=1 11 | W32FLAGS = -O3 -m32 -pthread -DWINDOWS -D_LP32=1 12 | C32FLAGS = -Wall -O3 -m32 -fPIC -pthread -DLINUX -D_LP32=1 13 | #-fPIC -g #-pedantic -Wall -Wextra -march=native -ggdb3 14 | LDFLAGS = -shared 15 | #LDFLAGS = -static-libgcc -shared -lc 16 | 17 | DEBUGFLAGS = -O0 -D _DEBUG 18 | RELEASEFLAGS = -O2 -D NDEBUG -combine -fwhole-program 19 | INCLUDES = -I $(JAVA_HOME)/include -I $(JAVA_HOME)/include/linux 20 | DISTDIR = ./target/native 21 | L64TARGET = -o ${DISTDIR}/linux64/liboifagent.so 22 | L32TARGET = -o ${DISTDIR}/linux32/liboifagent.so 23 | W64TARGET = -o ${DISTDIR}/win64/oifagent.dll 24 | W32TARGET = -o ${DISTDIR}/win32/oifagent.dll 25 | 26 | # /usr/bin/i686-w64-mingw32-c++ 27 | # /usr/bin/x86_64-w64-mingw32-g++ 28 | 29 | 30 | 31 | 32 | linux_native-agent-64: ./src/main/cpp/native-agent.cpp distdirl64 33 | ${LCPP} ${C64FLAGS} ${LDFLAGS} ${INCLUDES} ${L64TARGET} ./src/main/cpp/native-agent.cpp 34 | 35 | linux_native-agent-32: ./src/main/cpp/native-agent.cpp distdirl32 36 | ${LCPP} ${C32FLAGS} ${LDFLAGS} ${INCLUDES} ${L32TARGET} ./src/main/cpp/native-agent.cpp 37 | 38 | 39 | windows_native-agent-64: ./src/main/cpp/native-agent.cpp distdirw64 40 | ${W64CPP} ${W64FLAGS} ${LDFLAGS} -static-libstdc++ -static-libgcc ${INCLUDES} ${W64TARGET} ./src/main/cpp/native-agent.cpp 41 | 42 | windows_native-agent-32: ./src/main/cpp/native-agent.cpp distdirw32 43 | ${W32CPP} ${W32FLAGS} ${LDFLAGS} ${INCLUDES} ${W32TARGET} ./src/main/cpp/native-agent.cpp 44 | 45 | all: linux_native-agent-64 linux_native-agent-32 windows_native-agent-64 windows_native-agent-32 46 | 47 | distdirl64: 48 | test -d ${DISTDIR}/linux64 || mkdir -p ${DISTDIR}/linux64 49 | 50 | distdirl32: 51 | test -d ${DISTDIR}/linux32 || mkdir -p ${DISTDIR}/linux32 52 | 53 | distdirw64: 54 | test -d ${DISTDIR}/win64 || mkdir -p ${DISTDIR}/win64 55 | 56 | distdirw32: 57 | test -d ${DISTDIR}/win32 || mkdir -p ${DISTDIR}/win32 58 | 59 | clean: 60 | rm -rf ./target/native 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NativeJavaAgent 2 | The NativeJavaAgent is a JVMTI library supported by a Java library that allows you to: 3 | * Get a count of the number of objects in the heap of a specific type or a type and any type that inherit/implement from that type. 4 | * Acquire an object array of all the objects in the heap of a specific type or a type and any type that inherit/implement from that type. 5 | 6 | ### Instance Counts 7 | 8 | Here's an simple example of getting instance counts: 9 | 10 | ```java 11 | import com.heliosapm.jvmti.agent.Agent; 12 | 13 | public static void main(String[] args) { 14 | 15 | final Agent agent = Agent.getInstance(); 16 | System.gc(); // a lot of garbage created at boot time 17 | final long startTime = System.currentTimeMillis(); 18 | 19 | final int objectCount = agent.getInstanceCountOf(Object.class); 20 | log("Object instance count:%s", objectCount); 21 | 22 | final int charSequenceCount = agent.getInstanceCountOfAny(CharSequence.class); 23 | log("CharSequence instance count:%s", charSequenceCount); 24 | 25 | log("Elapsed:%s ms.", System.currentTimeMillis()-startTime); 26 | } 27 | ``` 28 | 29 | The output is: 30 | 31 | ``` 32 | Initializing Agent OnAttach... 33 | Agent Initialized 34 | Object instance count:467 35 | CharSequence instance count:3196 36 | Elapsed:3 ms. 37 | ``` 38 | 39 | What's going on here: 40 | 41 | 1. Acquire the (singleton) agent using `com.heliosapm.jvmti.agent.Agent.getInstance()`. In this case, the underlying native JVMTI library was loaded at runtime. The library can be loaded at boot time using `-agentpath:oif_agent.so` or if not loaded on boot, it will be loaded dynamically when the agent is first called. 42 | 2. Calling `agent.getInstanceCountOf(Object.class)` returns the number of Object instances found in the heap. The `getInstanceCountOf` method only counts objects of the exact type so it will not count instances of any type inherrited from `java.lang.Object` (basically everything else). 43 | 3. On the other hamd, in the next call, `agent.getInstanceCountOfAny(CharSequence.class)`, counts all instances in the heap that extend `java.lang.CharSequence`. 44 | 4. Since there's not much else going on in the JVM, the number of object instances is fairly small, but agent is fairly quick. Even so, warmup makes a difference. If I add this code to the example above: 45 | 46 | ```java 47 | int total = 0; 48 | for(int i = 0; i < 100; i++) { 49 | final int oc = agent.getInstanceCountOf(Object.class); 50 | final int cc = agent.getInstanceCountOfAny(CharSequence.class); 51 | total += (oc + cc); 52 | System.gc(); 53 | } 54 | 55 | final long startTime2 = System.currentTimeMillis(); 56 | 57 | final int objectCount2 = agent.getInstanceCountOf(Object.class); 58 | log("Object instance count:%s", objectCount2); 59 | 60 | final int charSequenceCount2 = agent.getInstanceCountOfAny(CharSequence.class); 61 | log("CharSequence instance count:%s", charSequenceCount2); 62 | 63 | log("Elapsed:%s ms.", System.currentTimeMillis()-startTime2); 64 | 65 | ``` 66 | 67 | .... then the output of the second timing is: 68 | 69 | ``` 70 | Object instance count:450 71 | CharSequence instance count:3174 72 | Elapsed:1 ms. 73 | ``` 74 | 75 | Note that background activity and the agent itself generate some number of objects, so for "accurate" counts, I am calling System.gc() at specific points so we're not counting unreachable but uncleared objects. 76 | 77 | ### Instance References 78 | 79 | In this example, the agent acquires the actual references to the first 20 `java.lang.String` instances found on the heap. The maximum number of instances supplied as 20 is optional. If not supplied, it will default to `Integer.MAX_VALUE`. It then prints a selection of those strings so we can see examples of the sort of strings hanging out in the heap. 80 | 81 | ```java 82 | public static void main(String[] args) { 83 | final Agent agent = Agent.getInstance(); 84 | System.gc(); 85 | String[] strings = agent.getInstancesOf(String.class, 20); 86 | for(int i = 10; i < 15; i++) { 87 | log("String #%s: [%s]", i, strings[i]); 88 | } 89 | strings = null; // Don't prevent gc of these objects ! 90 | } 91 | 92 | ``` 93 | Your output will vary, but in my last test I saw: 94 | 95 | ``` 96 | Initializing Agent OnAttach... 97 | Agent Initialized 98 | Aborting Instance Tagging after 20 Instances 99 | String #10: [Unexpected vector type encounterd: entry_offset = ] 100 | String #11: [ (0x] 101 | String #12: [), units = ] 102 | String #13: [Unexpected variability attribute: entry_offset = 0x] 103 | String #14: [ name = ] 104 | ``` 105 | 106 | As with the counting calls, the instance calls come in 2 flavours where `getInstancesOf` only retrieves objects of the exact supplied type, whereas the `getInstancesOfAny` retrieves instances of the specified type or any type that inherrits/extends that type. Repeating the same example for `CharSequence`s: 107 | 108 | ```java 109 | CharSequence[] charSeqs = agent.getInstancesOfAny(CharSequence.class, 20); 110 | for(int i = 10; i < 15; i++) { 111 | log("CharSequence#%s: Type:%s, Value:[%s]", 112 | i, charSeqs[i].getClass().getName(), charSeqs[i].toString()); 113 | } 114 | charSeqs = null; 115 | ``` 116 | 117 | The output: 118 | 119 | ``` 120 | CharSequence#10: Type:java.lang.String, Value:[%(\d+\$)?([-#+ 0,(\<]*)?(\d+)?(\.\d+)?([tT])?([a-zA-Z%])] 121 | CharSequence#11: Type:java.lang.String, Value:[DISPLAY] 122 | CharSequence#12: Type:java.lang.String, Value:[user.language.display] 123 | CharSequence#13: Type:java.lang.String, Value:[user.script.display] 124 | CharSequence#14: Type:java.lang.String, Value:[user.country.display] 125 | ``` 126 | 127 | ### Top N Type Instances 128 | 129 | Top N supports finding the types for which there are the most instances, so you can determine what your top *n* types in the heap are. For example: 130 | 131 | ```java 132 | public static void main(String[] args) { 133 | final Agent agent = Agent.getInstance(); 134 | System.gc(); 135 | final Map, Long> topMap = agent.getTopNInstanceCounts(Object.class, 10, true); 136 | for(Map.Entry, Long> entry: topMap.entrySet()) { 137 | log("%s : %s", Agent.renderClassName(entry.getKey()), entry.getValue()); 138 | } 139 | topMap.clear(); // don't prevent gc ! 140 | } 141 | ``` 142 | 143 | My output was: 144 | 145 | ``` 146 | Initializing Agent OnAttach... 147 | Agent Initialized 148 | java.lang.String : 3171 149 | java.lang.Class : 1105 150 | java.lang.Object[] : 1052 151 | java.lang.reflect.Method : 550 152 | java.lang.Object : 467 153 | java.util.concurrent.ConcurrentHashMap$Node : 424 154 | java.util.HashMap$Node : 418 155 | java.lang.Class[] : 395 156 | java.lang.String[] : 313 157 | java.lang.reflect.Field : 295 158 | ``` 159 | 160 | Note that the `Agent.renderClassName` method switches the internal array type names to the more visually clear **<type>[]...** syntax. 161 | 162 | The getTopNInstanceCounts parameters are: 163 | 164 | 1. The type. Matching is *Any* since specific type matching is not useful. 165 | 2. The *n* in TopN. 166 | 3. Indicates if primitives and primitive array types should be ignored in computing the TopN. 167 | 168 | Ignoring primitives and primitve arrays can make a big difference when values of *N* are small. In the example above, if we switch to not ignoring.... 169 | 170 | ``` 171 | Initializing Agent OnAttach... 172 | Agent Initialized 173 | char[] : 3185 174 | java.lang.String : 3171 175 | java.lang.Class : 1105 176 | java.lang.Object[] : 1052 177 | java.lang.reflect.Method : 550 178 | java.lang.Object : 467 179 | java.util.concurrent.ConcurrentHashMap$Node : 424 180 | java.util.HashMap$Node : 418 181 | java.lang.Class[] : 395 182 | java.lang.String[] : 313 183 | ``` 184 | 185 | which is not too bad, but for real applications, you may find byte and byte arrays dominate instance counts. Plus, all (non-empty?) strings will always contain `char[]` instances so will typically outnumber Strings. -------------------------------------------------------------------------------- /Test.java: -------------------------------------------------------------------------------- 1 | class Test { 2 | public static void main(String[] args) { 3 | System.out.println("Hello World"); 4 | int a = countInstances(Thread.class); 5 | System.out.println("There are " + a + " instances of " + Thread.class); 6 | Object[] objs = getAllInstances(Thread.class, System.nanoTime()); 7 | System.out.println("Arr Length:" + objs.length); 8 | System.out.println("Objects: " + java.util.Arrays.toString(objs)); 9 | } 10 | 11 | private static native int countInstances(Class klass); 12 | private static native Object[] getAllInstances(Class klass, long tag); 13 | } -------------------------------------------------------------------------------- /notes.txt: -------------------------------------------------------------------------------- 1 | 2 | - Fix method names 3 | - get instance filters 4 | - package native libraries in jar 5 | - lib installer and loader and cleaner 6 | - agent installer 7 | - sysprop/env overrides for native lib 8 | - counts, card for class name 9 | - sorted counts 10 | - topN counts 11 | - Find all classes by class name 12 | 13 | 14 | XML Config: 15 | External Config 16 | JMXMP Listener 17 | Hotspot MBeans 18 | External Scripts Dir 19 | Scripts to Load 20 | Fixtures (e.g. MBeans for located thread pools + metrics) 21 | External Native Lib Override 22 | Detect native arch 23 | Built in scripts 24 | External Logging Config (first ?) 25 | Set sysprops 26 | 27 | Extensions: 28 | Interface / Abstract Base 29 | Scheduler 30 | ThreadPools - Counts, Active, Thread Stats 31 | NIO Buffers - By type, Counts, Total Allocation 32 | Sockets 33 | 34 | 35 | Needed for linux build: 36 | libc6-dev-i386 37 | gcc-multilib 38 | g++-multilib 39 | mingw-w64 40 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | com.heliosapm.jvmti 5 | native-agent 6 | 1.0-SNAPSHOT 7 | jar 8 | HeliosAPM :: jvmti agent 9 | https://github.com/nickman/NativeJavaAgent 10 | 11 | 12 | UTF-8 13 | 1.8 14 | 1.8 15 | 16 | 17 | 4.8.2 18 | 19 | 3.2.5 20 | 1.2 21 | 22 | 1.2 23 | 2.1.1 24 | 3.12.1.GA 25 | 1.0.0-rc.1 26 | 27 | 2.3.2 28 | 2.3 29 | 1.8 30 | 2.2 31 | 2.9 32 | 2.4 33 | 2.9 34 | 2.16 35 | 2.5.1 36 | 1.0-alpha-8 37 | 1.3.2 38 | 3.1.0 39 | 40 | /usr/lib/jvm/jdk1.8.0_31 41 | 42 | 43 | 44 | 45 | 46 | src/main/resources 47 | 48 | 49 | target/native 50 | 51 | 52 | 53 | 54 | 55 | 56 | org.apache.maven.plugins 57 | maven-compiler-plugin 58 | ${compiler-plugin.version} 59 | 60 | ${compiler-source.version} 61 | ${compiler-target.version} 62 | 63 | 64 | 65 | 66 | org.apache.maven.plugins 67 | maven-eclipse-plugin 68 | ${eclipse-plugin.version} 69 | 70 | false 71 | none 72 | true 73 | true 74 | 75 | 76 | 77 | 78 | org.apache.maven.plugins 79 | maven-source-plugin 80 | ${source-plugin.version} 81 | 82 | 83 | attach-sources 84 | verify 85 | 86 | jar 87 | 88 | 89 | 90 | 91 | 92 | 93 | org.apache.maven.plugins 94 | maven-javadoc-plugin 95 | ${source-plugin.version} 96 | 97 | 98 | attach-javadocs 99 | verify 100 | 101 | jar 102 | 103 | 104 | -Xdoclint:none 105 | 106 | 107 | 108 | 109 | public 110 | true 111 | 112 | 113 | 114 | 115 | 116 | org.apache.maven.plugins 117 | maven-antrun-plugin 118 | ${antrun.version} 119 | 120 | 121 | make 122 | generate-sources 123 | 124 | run 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | org.codehaus.mojo 140 | exec-maven-plugin 141 | ${exec-plugin.version} 142 | 143 | 144 | 145 | exec 146 | 147 | 148 | 149 | 150 | java 151 | ${basedir} 152 | 153 | -agentpath:./agent.so 154 | -classpath 155 | ${basedir}/target/classes 156 | com.heliosapm.jvmti.agent.Agent 157 | 158 | 159 | 160 | 161 | 162 | org.apache.maven.plugins 163 | maven-jar-plugin 164 | ${jar-plugin.version} 165 | 166 | 167 | 168 | true 169 | true 170 | true 171 | 172 | 173 | com.heliosapm.jvmti.install.AgentInstaller 174 | 175 | 176 | com.heliosapm.jvmti.install.JavaAgent2 177 | com.heliosapm.jvmti.install.JavaAgent2 178 | true 179 | true 180 | true 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | org.apache.maven.plugins 189 | maven-shade-plugin 190 | ${shade.version} 191 | 192 | 193 | package 194 | 195 | shade 196 | 197 | 198 | 199 | 200 | com.codahale.metrics 201 | helios.metrics 202 | 203 | 204 | io.netty 205 | helios.netty 206 | 207 | 208 | org.pmw 209 | helios.pmw 210 | 211 | 212 | org.jctools 213 | helios.jctools 214 | 215 | 216 | javassist 217 | helios.javassist 218 | 219 | 220 | org.slf4j 221 | helios.org.slf4j 222 | 223 | 224 | 225 | 226 | META-INF/*.SF 227 | META-INF/*.DSA 228 | META-INF/*.RSA 229 | 230 | 231 | 232 | 233 | 234 | true 235 | true 236 | false 237 | 238 | 239 | 240 | 241 | .SF 242 | .DSA 243 | .RSA 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | junit 262 | junit 263 | ${junit.version} 264 | test 265 | 266 | 267 | com.heliosapm.utils 268 | heliosutils 269 | ${utils.version} 270 | 271 | 272 | 273 | 274 | io.micrometer 275 | micrometer-core 276 | ${micrometer.version} 277 | 278 | 279 | 280 | io.micrometer 281 | micrometer-registry-prometheus 282 | ${micrometer.version} 283 | 284 | 285 | 286 | javassist 287 | javassist 288 | ${javassist.version} 289 | 290 | 291 | 292 | io.dropwizard.metrics 293 | metrics-core 294 | ${metrics.version} 295 | 296 | 297 | org.slf4j 298 | slf4j-api 299 | 300 | 301 | 302 | 303 | io.dropwizard.metrics 304 | metrics-jvm 305 | ${metrics.version} 306 | 307 | 308 | 309 | org.tinylog 310 | tinylog 311 | ${tinylog.version} 312 | 313 | 314 | org.jctools 315 | jctools-core 316 | ${jctools.version} 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | -------------------------------------------------------------------------------- /src/main/cpp/native-agent-old.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | 9 | using namespace std; 10 | 11 | typedef struct { 12 | jvmtiEnv *jvmti; 13 | } GlobalAgentData; 14 | 15 | typedef struct { 16 | int tagCount; 17 | int tagMax; 18 | jlong* tag; 19 | } TagContext; 20 | 21 | 22 | static GlobalAgentData *gdata; 23 | static bool onLoad; 24 | 25 | 26 | static jobject callbacksInstance; 27 | static jmethodID callbackIncrementMethod; 28 | static jmethodID callbackCompleteMethod; 29 | static JavaVM *jvm; 30 | 31 | 32 | extern "C" 33 | JNIEXPORT jboolean JNICALL Java_com_heliosapm_jvmti_agent_NativeAgent_initCallbacks0(JNIEnv *env, jclass thisClass, jobject callbackSite) { 34 | env->GetJavaVM(&jvm); 35 | callbacksInstance = env->NewGlobalRef(callbackSite); 36 | callbackIncrementMethod = env->GetMethodID(thisClass, "increment", "(JLjava/lang/Object;)V"); 37 | callbackCompleteMethod = env->GetMethodID(thisClass, "complete", "(J)V"); 38 | cout << "Callback increment method:" << callbacksInstance << "->" << callbackIncrementMethod << endl; 39 | cout << "Callback complete method:" << callbacksInstance << "->" << callbackCompleteMethod << endl; 40 | return true; 41 | } 42 | 43 | extern "C" 44 | JNICALL jvmtiIterationControl typeInstanceCountingCallback(jlong class_tag, jlong size, jlong* tag_ptr, void* user_data) { 45 | TagContext* ctx = (TagContext*) user_data; 46 | if(ctx->tagMax!=0 && ctx->tagCount >= ctx->tagMax) { 47 | cout << "Aborting Instance Tagging after " << ctx->tagCount << " Instances" << endl; 48 | return JVMTI_ITERATION_ABORT; 49 | } 50 | ctx->tagCount++; 51 | *tag_ptr = *ctx->tag; 52 | return JVMTI_ITERATION_CONTINUE; 53 | } 54 | 55 | 56 | // public void complete(final long tag) { 57 | // public void increment(final long tag, final Object obj) { 58 | 59 | 60 | extern "C" 61 | JNIEXPORT void JNICALL Java_com_heliosapm_jvmti_agent_NativeAgent_typeCardinality0(JNIEnv *env, jclass thisClass, jclass targetClass, jlong tg, jint max) { 62 | 63 | TagContext* ctx = new TagContext(); 64 | ctx->tagCount = 0; 65 | ctx->tagMax = max; 66 | ctx->tag = &tg; 67 | 68 | jvmtiCapabilities capabilities = {0}; 69 | capabilities.can_tag_objects = 1; 70 | gdata->jvmti->AddCapabilities(&capabilities); 71 | 72 | gdata->jvmti->IterateOverInstancesOfClass(targetClass, JVMTI_HEAP_OBJECT_EITHER, &typeInstanceCountingCallback, ctx); 73 | jobject* objArr; 74 | jlong* tagArr; 75 | jvm->AttachCurrentThread((void **)&env, NULL); 76 | gdata->jvmti->GetObjectsWithTags(1, &tg, &ctx->tagCount, &objArr, &tagArr); 77 | for (int n=0; ntagCount; n++) { 78 | env->CallVoidMethod(callbacksInstance, callbackIncrementMethod, tg, objArr[n]); 79 | } 80 | cout << "Calling Callback Complete:" << ctx->tagCount << endl; 81 | env->CallVoidMethod(callbacksInstance, callbackCompleteMethod, tg); 82 | jvm->DetachCurrentThread(); 83 | gdata->jvmti->Deallocate((unsigned char*)objArr); 84 | gdata->jvmti->Deallocate((unsigned char*)tagArr); 85 | } 86 | 87 | // JNIEXPORT jobjectArray JNICALL Java_com_heliosapm_jvmti_agent_NativeAgent_getInstances0(JNIEnv* env, jclass ignored, jclass targetClass, jlong tg, jint max) { 88 | // TagContext* ctx = new TagContext(); 89 | // ctx->tagCount = 0; 90 | // ctx->tagMax = max; 91 | // ctx->tag = &tg; 92 | 93 | 94 | // jvmtiCapabilities capabilities = {0}; 95 | // capabilities.can_tag_objects = 1; 96 | // gdata->jvmti->AddCapabilities(&capabilities); 97 | 98 | // gdata->jvmti->IterateOverInstancesOfClass(targetClass, JVMTI_HEAP_OBJECT_EITHER, &typeInstanceCountingCallback, ctx); 99 | 100 | // jobject* objArr; 101 | // jlong* tagArr; 102 | // //jvmtiError errorGet = 103 | // gdata->jvmti->GetObjectsWithTags(1, &tg, &ctx->tagCount, &objArr, &tagArr); 104 | // jobjectArray ret = env->NewObjectArray(ctx->tagCount, targetClass, NULL); 105 | // for (int n=0; ntagCount; n++) { 106 | // env->SetObjectArrayElement(ret, n, objArr[n]); 107 | // } 108 | // gdata->jvmti->Deallocate((unsigned char*)objArr); 109 | // gdata->jvmti->Deallocate((unsigned char*)tagArr); 110 | // return ret; 111 | 112 | 113 | 114 | extern "C" 115 | JNIEXPORT jboolean JNICALL Java_com_heliosapm_jvmti_agent_NativeAgent_wasLoaded0(JNIEnv *env, jclass thisClass) { 116 | return onLoad; 117 | } 118 | 119 | 120 | JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* jvm, char *options, void *reserved) { 121 | cout << "Initializing Agent OnAttach..." << endl; 122 | onLoad = false; 123 | jvmtiEnv *jvmti = NULL; 124 | jvmtiCapabilities capa; 125 | //jvmtiError error; 126 | 127 | // put a jvmtiEnv instance at jvmti. 128 | jint result = jvm->GetEnv((void **) &jvmti, JVMTI_VERSION_1_2); 129 | if (result != JNI_OK) { 130 | printf("ERROR: Unable to access JVMTI!\n"); 131 | } 132 | // add a capability to tag objects 133 | (void)memset(&capa, 0, sizeof(jvmtiCapabilities)); 134 | capa.can_tag_objects = 1; 135 | capa.can_generate_compiled_method_load_events = 1; 136 | (jvmti)->AddCapabilities(&capa); 137 | 138 | // store jvmti in a global data 139 | gdata = new GlobalAgentData(); 140 | //(GlobalAgentData*) malloc(sizeof(GlobalAgentData)); 141 | gdata->jvmti = jvmti; 142 | cout << "Agent Initialized" << endl; 143 | 144 | return JNI_OK; 145 | 146 | } 147 | 148 | JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) { 149 | cout << "Initializing Agent OnLoad..." << endl; 150 | onLoad = true; 151 | jvmtiEnv *jvmti = NULL; 152 | jvmtiCapabilities capa; 153 | //jvmtiError error; 154 | 155 | // put a jvmtiEnv instance at jvmti. 156 | jint result = jvm->GetEnv((void **) &jvmti, JVMTI_VERSION_1_2); 157 | if (result != JNI_OK) { 158 | printf("ERROR: Unable to access JVMTI!\n"); 159 | } 160 | // add a capability to tag objects 161 | (void)memset(&capa, 0, sizeof(jvmtiCapabilities)); 162 | capa.can_tag_objects = 1; 163 | capa.can_generate_compiled_method_load_events = 1; 164 | (jvmti)->AddCapabilities(&capa); 165 | 166 | // store jvmti in a global data 167 | gdata = new GlobalAgentData(); 168 | //(GlobalAgentData*) malloc(sizeof(GlobalAgentData)); 169 | gdata->jvmti = jvmti; 170 | cout << "Agent Initialized" << endl; 171 | 172 | return JNI_OK; 173 | } 174 | 175 | 176 | 177 | 178 | extern "C" 179 | JNICALL jint objectCountingCallback(jlong class_tag, jlong size, jlong* tag_ptr, jint length, void* user_data) { 180 | int* count = (int*) user_data; 181 | *count += 1; 182 | return JVMTI_VISIT_OBJECTS; 183 | } 184 | 185 | 186 | 187 | extern "C" 188 | JNIEXPORT jint JNICALL Java_com_heliosapm_jvmti_agent_NativeAgent_countExactInstances0(JNIEnv *env, jclass thisClass, jclass klass) { 189 | int count = 0; 190 | jvmtiHeapCallbacks callbacks; 191 | (void)memset(&callbacks, 0, sizeof(callbacks)); 192 | callbacks.heap_iteration_callback = &objectCountingCallback; 193 | //jvmtiError error = 194 | gdata->jvmti->IterateThroughHeap(0, klass, &callbacks, &count); 195 | return count; 196 | } 197 | 198 | 199 | extern "C" 200 | JNICALL jint objectTaggingCallback(jlong class_tag, jlong size, jlong* tag_ptr, jint length, void* user_data) { 201 | TagContext* ctx = (TagContext*) user_data; 202 | if(ctx->tagMax!=0 && ctx->tagCount >= ctx->tagMax) { 203 | cout << "Aborting Instance Tagging after " << ctx->tagCount << " Instances" << endl; 204 | return JVMTI_VISIT_ABORT; 205 | } 206 | ctx->tagCount++; 207 | *tag_ptr = *ctx->tag; 208 | return JVMTI_VISIT_OBJECTS; 209 | } 210 | 211 | 212 | 213 | extern "C" 214 | JNIEXPORT jobjectArray JNICALL Java_com_heliosapm_jvmti_agent_NativeAgent_getExactInstances0(JNIEnv *env, jclass thisClass, jclass klass, jlong tag, jint max) { 215 | jvmtiHeapCallbacks callbacks; 216 | (void)memset(&callbacks, 0, sizeof(callbacks)); 217 | callbacks.heap_iteration_callback = &objectTaggingCallback; 218 | TagContext* ctx = new TagContext(); 219 | ctx->tagCount = 0; 220 | ctx->tagMax = max; 221 | ctx->tag = &tag; 222 | //jvmtiError error = 223 | gdata->jvmti->IterateThroughHeap(0, klass, &callbacks, ctx); 224 | jobject* objArr; 225 | jlong* tagArr; 226 | //jvmtiError errorGet = 227 | gdata->jvmti->GetObjectsWithTags(1, &tag, &ctx->tagCount, &objArr, &tagArr); 228 | jobjectArray ret = env->NewObjectArray(ctx->tagCount, klass, NULL); 229 | for (int n=0; ntagCount; n++) { 230 | env->SetObjectArrayElement(ret, n, objArr[n]); 231 | } 232 | gdata->jvmti->Deallocate((unsigned char*)objArr); 233 | gdata->jvmti->Deallocate((unsigned char*)tagArr); 234 | return ret; 235 | } 236 | 237 | 238 | extern "C" 239 | JNIEXPORT int JNICALL Java_com_heliosapm_jvmti_agent_NativeAgent_countInstances0(JNIEnv* env, jclass ignored, jclass targetClass, jlong tg, jint max) { 240 | 241 | TagContext* ctx = new TagContext(); 242 | ctx->tagCount = 0; 243 | ctx->tagMax = max; 244 | ctx->tag = &tg; 245 | 246 | 247 | jvmtiCapabilities capabilities = {0}; 248 | capabilities.can_tag_objects = 1; 249 | gdata->jvmti->AddCapabilities(&capabilities); 250 | 251 | gdata->jvmti->IterateOverInstancesOfClass(targetClass, JVMTI_HEAP_OBJECT_EITHER, &typeInstanceCountingCallback, ctx); 252 | 253 | return ctx->tagCount; 254 | } 255 | 256 | 257 | extern "C" 258 | JNIEXPORT jobjectArray JNICALL Java_com_heliosapm_jvmti_agent_NativeAgent_getInstances0(JNIEnv* env, jclass ignored, jclass targetClass, jlong tg, jint max) { 259 | TagContext* ctx = new TagContext(); 260 | ctx->tagCount = 0; 261 | ctx->tagMax = max; 262 | ctx->tag = &tg; 263 | 264 | 265 | jvmtiCapabilities capabilities = {0}; 266 | capabilities.can_tag_objects = 1; 267 | gdata->jvmti->AddCapabilities(&capabilities); 268 | 269 | gdata->jvmti->IterateOverInstancesOfClass(targetClass, JVMTI_HEAP_OBJECT_EITHER, &typeInstanceCountingCallback, ctx); 270 | 271 | jobject* objArr; 272 | jlong* tagArr; 273 | //jvmtiError errorGet = 274 | gdata->jvmti->GetObjectsWithTags(1, &tg, &ctx->tagCount, &objArr, &tagArr); 275 | jobjectArray ret = env->NewObjectArray(ctx->tagCount, targetClass, NULL); 276 | for (int n=0; ntagCount; n++) { 277 | env->SetObjectArrayElement(ret, n, objArr[n]); 278 | } 279 | gdata->jvmti->Deallocate((unsigned char*)objArr); 280 | gdata->jvmti->Deallocate((unsigned char*)tagArr); 281 | return ret; 282 | } 283 | 284 | 285 | 286 | -------------------------------------------------------------------------------- /src/main/cpp/native-agent.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | 9 | /* 10 | 11 | +-------------------------------------------------------------------------------------------------------+ 12 | |XXXXXXXXXXXXXXXXXXXXXXXXXXX| Exact Type || Inherrited | 13 | +-------------------------------------------------------------------------------------------------------+ 14 | | || countExactInstances0 || countInstances0 | 15 | | Count Instances || objectCountingCallback || typeInstanceCountingCallback | 16 | | || || | 17 | +-------------------------------------------------------------------------------------------------------+ 18 | | ||XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX|| typeCardinality0 | 19 | | Type Cardinality ||XXX Same as count exact XXXXXXX|| typeInstanceCountingCallback | 20 | | ||XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX|| | 21 | +-------------------------------------------------------------------------------------------------------+ 22 | | || getExactInstances0 || getInstances0 | 23 | | Get Instances (for Arr) || objectTaggingCallback ---> || typeInstanceCountingCallback | // Inherrited can use object tagging 24 | | || || | 25 | +-------------------------------------------------------------------------------------------------------+ 26 | | || queueExactInstances0 || queueInstances0 | // ASYNC ! 27 | | Get Instances (Queue) || objectTaggingCallback || objectTaggingCallback | 28 | | || || | 29 | +-------------------------------------------------------------------------------------------------------+ 30 | 31 | * Object Size Support 32 | * get class id 33 | * get method id 34 | 35 | */ 36 | 37 | 38 | 39 | 40 | using namespace std; 41 | 42 | static const jlong CLEAR_TAG = 0x00000000; 43 | 44 | 45 | typedef struct { 46 | jvmtiEnv *jvmti; 47 | } GlobalAgentData; 48 | 49 | typedef struct { 50 | int tagCount; 51 | int tagMax; 52 | jlong* tag; 53 | jlong tsize; 54 | jobject* queue; 55 | } TagContext; 56 | 57 | 58 | 59 | static GlobalAgentData *gdata; 60 | static bool onLoad; 61 | 62 | 63 | static jobject callbacksInstance; 64 | static jmethodID callbackIncrementMethod; 65 | static jmethodID callbackCompleteMethod; 66 | static jmethodID queueAddMethod; 67 | static jclass queueClazz; 68 | static jobject eoq; 69 | static JavaVM *jvm; 70 | 71 | 72 | extern "C" 73 | JNIEXPORT jboolean JNICALL Java_com_heliosapm_jvmti_agent_NativeAgent_initCallbacks0(JNIEnv *env, jclass thisClass, jobject callbackSite, jclass queueClass, jobject endOfQueue) { 74 | env->GetJavaVM(&jvm); 75 | callbacksInstance = env->NewGlobalRef(callbackSite); 76 | callbackIncrementMethod = env->GetMethodID(thisClass, "increment", "(JLjava/lang/Object;)V"); 77 | callbackCompleteMethod = env->GetMethodID(thisClass, "complete", "(J)V"); 78 | queueClazz = queueClass; 79 | eoq = env->NewGlobalRef(endOfQueue); 80 | queueAddMethod = env->GetMethodID(queueClazz, "offer", "(Ljava/lang/Object;)Z"); 81 | cout << "Callback increment method:" << callbacksInstance << "->" << callbackIncrementMethod << endl; 82 | cout << "Callback complete method:" << callbacksInstance << "->" << callbackCompleteMethod << endl; 83 | cout << "Queue add method:" << queueClazz << "->" << queueAddMethod << endl; 84 | return true; 85 | } 86 | 87 | extern "C" 88 | JNICALL jvmtiIterationControl typeInstanceCountingCallback(jlong class_tag, jlong size, jlong* tag_ptr, void* user_data) { 89 | TagContext* ctx = (TagContext*) user_data; 90 | if(ctx->tagMax!=0 && ctx->tagCount >= ctx->tagMax) { 91 | cout << "Aborting Instance Tagging after " << ctx->tagCount << " Instances" << endl; 92 | return JVMTI_ITERATION_ABORT; 93 | } 94 | ctx->tagCount++; 95 | *tag_ptr = *ctx->tag; 96 | return JVMTI_ITERATION_CONTINUE; 97 | } 98 | 99 | 100 | 101 | 102 | // jobject* queue; 103 | // env->CallVoidMethod(callbacksInstance, callbackIncrementMethod, tg, objArr[n]); 104 | // public void complete(final long tag) { 105 | // public void increment(final long tag, final Object obj) { 106 | 107 | 108 | extern "C" 109 | JNIEXPORT void JNICALL Java_com_heliosapm_jvmti_agent_NativeAgent_typeCardinality0(JNIEnv *env, jclass thisClass, jclass targetClass, jlong tg, jint max) { 110 | 111 | TagContext* ctx = new TagContext(); 112 | ctx->tagCount = 0; 113 | ctx->tagMax = max; 114 | ctx->tag = &tg; 115 | 116 | jvmtiCapabilities capabilities = {0}; 117 | capabilities.can_tag_objects = 1; 118 | gdata->jvmti->AddCapabilities(&capabilities); 119 | 120 | gdata->jvmti->IterateOverInstancesOfClass(targetClass, JVMTI_HEAP_OBJECT_EITHER, &typeInstanceCountingCallback, ctx); 121 | jobject* objArr; 122 | jlong* tagArr; 123 | jvm->AttachCurrentThread((void **)&env, NULL); 124 | gdata->jvmti->GetObjectsWithTags(1, &tg, &ctx->tagCount, &objArr, &tagArr); 125 | for (int n=0; ntagCount; n++) { 126 | env->CallVoidMethod(callbacksInstance, callbackIncrementMethod, tg, objArr[n]); 127 | } 128 | cout << "Calling Callback Complete:" << ctx->tagCount << endl; 129 | env->CallVoidMethod(callbacksInstance, callbackCompleteMethod, tg); 130 | jvm->DetachCurrentThread(); 131 | gdata->jvmti->Deallocate((unsigned char*)objArr); 132 | gdata->jvmti->Deallocate((unsigned char*)tagArr); 133 | } 134 | 135 | // JNIEXPORT jobjectArray JNICALL Java_com_heliosapm_jvmti_agent_NativeAgent_getInstances0(JNIEnv* env, jclass ignored, jclass targetClass, jlong tg, jint max) { 136 | // TagContext* ctx = new TagContext(); 137 | // ctx->tagCount = 0; 138 | // ctx->tagMax = max; 139 | // ctx->tag = &tg; 140 | 141 | 142 | // jvmtiCapabilities capabilities = {0}; 143 | // capabilities.can_tag_objects = 1; 144 | // gdata->jvmti->AddCapabilities(&capabilities); 145 | 146 | // gdata->jvmti->IterateOverInstancesOfClass(targetClass, JVMTI_HEAP_OBJECT_EITHER, &typeInstanceCountingCallback, ctx); 147 | 148 | // jobject* objArr; 149 | // jlong* tagArr; 150 | // //jvmtiError errorGet = 151 | // gdata->jvmti->GetObjectsWithTags(1, &tg, &ctx->tagCount, &objArr, &tagArr); 152 | // jobjectArray ret = env->NewObjectArray(ctx->tagCount, targetClass, NULL); 153 | // for (int n=0; ntagCount; n++) { 154 | // env->SetObjectArrayElement(ret, n, objArr[n]); 155 | // } 156 | // gdata->jvmti->Deallocate((unsigned char*)objArr); 157 | // gdata->jvmti->Deallocate((unsigned char*)tagArr); 158 | // return ret; 159 | 160 | 161 | 162 | extern "C" 163 | JNIEXPORT jboolean JNICALL Java_com_heliosapm_jvmti_agent_NativeAgent_wasLoaded0(JNIEnv *env, jclass thisClass) { 164 | return onLoad; 165 | } 166 | 167 | 168 | JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM* jvm, char *options, void *reserved) { 169 | cout << "Initializing Agent OnAttach..." << endl; 170 | onLoad = false; 171 | jvmtiEnv *jvmti = NULL; 172 | jvmtiCapabilities capa; 173 | //jvmtiError error; 174 | 175 | // put a jvmtiEnv instance at jvmti. 176 | jint result = jvm->GetEnv((void **) &jvmti, JVMTI_VERSION_1_2); 177 | if (result != JNI_OK) { 178 | printf("ERROR: Unable to access JVMTI!\n"); 179 | } 180 | // add a capability to tag objects 181 | (void)memset(&capa, 0, sizeof(jvmtiCapabilities)); 182 | capa.can_tag_objects = 1; 183 | capa.can_generate_compiled_method_load_events = 1; 184 | (jvmti)->AddCapabilities(&capa); 185 | 186 | // store jvmti in a global data 187 | gdata = new GlobalAgentData(); 188 | //(GlobalAgentData*) malloc(sizeof(GlobalAgentData)); 189 | gdata->jvmti = jvmti; 190 | cout << "Agent Initialized" << endl; 191 | 192 | return JNI_OK; 193 | 194 | } 195 | 196 | JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved) { 197 | cout << "Initializing Agent OnLoad..." << endl; 198 | onLoad = true; 199 | jvmtiEnv *jvmti = NULL; 200 | jvmtiCapabilities capa; 201 | //jvmtiError error; 202 | 203 | // put a jvmtiEnv instance at jvmti. 204 | jint result = jvm->GetEnv((void **) &jvmti, JVMTI_VERSION_1_2); 205 | if (result != JNI_OK) { 206 | printf("ERROR: Unable to access JVMTI!\n"); 207 | } 208 | // add a capability to tag objects 209 | (void)memset(&capa, 0, sizeof(jvmtiCapabilities)); 210 | capa.can_tag_objects = 1; 211 | capa.can_generate_compiled_method_load_events = 1; 212 | (jvmti)->AddCapabilities(&capa); 213 | 214 | // store jvmti in a global data 215 | gdata = new GlobalAgentData(); 216 | //(GlobalAgentData*) malloc(sizeof(GlobalAgentData)); 217 | gdata->jvmti = jvmti; 218 | cout << "Agent Initialized" << endl; 219 | 220 | return JNI_OK; 221 | } 222 | 223 | 224 | 225 | 226 | extern "C" 227 | JNICALL jint objectCountingCallback(jlong class_tag, jlong size, jlong* tag_ptr, jint length, void* user_data) { 228 | int* count = (int*) user_data; 229 | *count += 1; 230 | return JVMTI_VISIT_OBJECTS; 231 | } 232 | 233 | 234 | 235 | extern "C" 236 | JNIEXPORT jint JNICALL Java_com_heliosapm_jvmti_agent_NativeAgent_countExactInstances0(JNIEnv *env, jclass thisClass, jclass klass) { 237 | int count = 0; 238 | jvmtiHeapCallbacks callbacks; 239 | (void)memset(&callbacks, 0, sizeof(callbacks)); 240 | callbacks.heap_iteration_callback = &objectCountingCallback; 241 | //jvmtiError error = 242 | gdata->jvmti->IterateThroughHeap(0, klass, &callbacks, &count); 243 | return count; 244 | } 245 | 246 | 247 | extern "C" 248 | JNICALL jint objectTaggingCallback(jlong class_tag, jlong size, jlong* tag_ptr, jint length, void* user_data) { 249 | TagContext* ctx = (TagContext*) user_data; 250 | if(ctx->tagMax!=0 && ctx->tagCount >= ctx->tagMax) { 251 | //cout << "Aborting Instance Tagging after " << ctx->tagCount << " Instances for tag [" << *ctx->tag << "]" << endl; 252 | return JVMTI_VISIT_ABORT; 253 | } 254 | ctx->tagCount++; 255 | *tag_ptr = *ctx->tag; 256 | return JVMTI_VISIT_OBJECTS; 257 | } 258 | 259 | 260 | 261 | extern "C" 262 | JNIEXPORT jobjectArray JNICALL Java_com_heliosapm_jvmti_agent_NativeAgent_getExactInstances0(JNIEnv *env, jclass thisClass, jclass klass, jlong tag, jint max) { 263 | jvmtiHeapCallbacks callbacks; 264 | (void)memset(&callbacks, 0, sizeof(callbacks)); 265 | callbacks.heap_iteration_callback = &objectTaggingCallback; 266 | TagContext* ctx = new TagContext(); 267 | ctx->tagCount = 0; 268 | ctx->tagMax = max; 269 | ctx->tag = &tag; 270 | //jvmtiError error = 271 | gdata->jvmti->IterateThroughHeap(0, klass, &callbacks, ctx); 272 | jobject* objArr; 273 | jlong* tagArr; 274 | //jvmtiError errorGet = 275 | gdata->jvmti->GetObjectsWithTags(1, &tag, &ctx->tagCount, &objArr, &tagArr); 276 | jobjectArray ret = env->NewObjectArray(ctx->tagCount, klass, NULL); 277 | for (int n=0; ntagCount; n++) { 278 | env->SetObjectArrayElement(ret, n, objArr[n]); 279 | } 280 | gdata->jvmti->Deallocate((unsigned char*)objArr); 281 | gdata->jvmti->Deallocate((unsigned char*)tagArr); 282 | return ret; 283 | } 284 | 285 | extern "C" 286 | JNIEXPORT int JNICALL Java_com_heliosapm_jvmti_agent_NativeAgent_queueExactInstances0(JNIEnv *env, jclass thisClass, jclass klass, jlong tag, jint max, jobject queue) { 287 | jvmtiHeapCallbacks callbacks; 288 | (void)memset(&callbacks, 0, sizeof(callbacks)); 289 | callbacks.heap_iteration_callback = &objectTaggingCallback; 290 | TagContext* ctx = new TagContext(); 291 | ctx->tagCount = 0; 292 | ctx->tagMax = max; 293 | ctx->tag = &tag; 294 | gdata->jvmti->IterateThroughHeap(0, klass, &callbacks, ctx); 295 | jobject* objArr; 296 | jlong* tagArr; 297 | gdata->jvmti->GetObjectsWithTags(1, &tag, &ctx->tagCount, &objArr, &tagArr); 298 | // cout << "Enqueueing [" << ctx->tagCount << "] objects for tag [" << tag << "]" << endl; 299 | for (int n=0; ntagCount; n++) { 300 | env->CallBooleanMethod(queue, queueAddMethod, objArr[n]); 301 | } 302 | bool complete = env->CallBooleanMethod(queue, queueAddMethod, eoq); 303 | // cout << "Queue Complete:" << complete << ", EOQ:" << eoq << endl; 304 | gdata->jvmti->Deallocate((unsigned char*)objArr); 305 | gdata->jvmti->Deallocate((unsigned char*)tagArr); 306 | return ctx->tagCount; 307 | } 308 | 309 | 310 | 311 | extern "C" 312 | JNIEXPORT int JNICALL Java_com_heliosapm_jvmti_agent_NativeAgent_countInstances0(JNIEnv* env, jclass ignored, jclass targetClass, jlong tg, jint max) { 313 | 314 | TagContext* ctx = new TagContext(); 315 | ctx->tagCount = 0; 316 | ctx->tagMax = max; 317 | ctx->tag = &tg; 318 | 319 | 320 | jvmtiCapabilities capabilities = {0}; 321 | capabilities.can_tag_objects = 1; 322 | gdata->jvmti->AddCapabilities(&capabilities); 323 | 324 | gdata->jvmti->IterateOverInstancesOfClass(targetClass, JVMTI_HEAP_OBJECT_EITHER, &typeInstanceCountingCallback, ctx); 325 | 326 | return ctx->tagCount; 327 | } 328 | 329 | 330 | extern "C" 331 | JNIEXPORT jobjectArray JNICALL Java_com_heliosapm_jvmti_agent_NativeAgent_getInstances0(JNIEnv* env, jclass ignored, jclass targetClass, jlong tg, jint max) { 332 | TagContext* ctx = new TagContext(); 333 | ctx->tagCount = 0; 334 | ctx->tagMax = max; 335 | ctx->tag = &tg; 336 | 337 | 338 | jvmtiCapabilities capabilities = {0}; 339 | capabilities.can_tag_objects = 1; 340 | gdata->jvmti->AddCapabilities(&capabilities); 341 | 342 | gdata->jvmti->IterateOverInstancesOfClass(targetClass, JVMTI_HEAP_OBJECT_EITHER, &typeInstanceCountingCallback, ctx); 343 | 344 | jobject* objArr; 345 | jlong* tagArr; 346 | //jvmtiError errorGet = 347 | gdata->jvmti->GetObjectsWithTags(1, &tg, &ctx->tagCount, &objArr, &tagArr); 348 | jobjectArray ret = env->NewObjectArray(ctx->tagCount, targetClass, NULL); 349 | for (int n=0; ntagCount; n++) { 350 | env->SetObjectArrayElement(ret, n, objArr[n]); 351 | } 352 | gdata->jvmti->Deallocate((unsigned char*)objArr); 353 | gdata->jvmti->Deallocate((unsigned char*)tagArr); 354 | return ret; 355 | } 356 | 357 | 358 | 359 | -------------------------------------------------------------------------------- /src/main/java/com/heliosapm/jvmti/agent/AbstractVirtualMachineTask.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2016 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package com.heliosapm.jvmti.agent; 14 | 15 | import com.heliosapm.shorthand.attach.vm.VirtualMachine; 16 | 17 | /** 18 | *

Title: AbstractVirtualMachineTask

19 | *

Description: An empty call virtual machine task for override

20 | *

Company: Helios Development Group LLC

21 | * @author Whitehead (nwhitehead AT heliosdev DOT org) 22 | *

com.heliosapm.jvmti.agent.AbstractVirtualMachineTask

23 | */ 24 | 25 | public abstract class AbstractVirtualMachineTask implements VirtualMachineTask { 26 | /** The virtual machine instance */ 27 | public VirtualMachine vm = null; 28 | 29 | /** 30 | * {@inheritDoc} 31 | * @see com.heliosapm.jvmti.agent.VirtualMachineTask#setVirtualMachine(com.heliosapm.shorthand.attach.vm.VirtualMachine) 32 | */ 33 | @Override 34 | public void setVirtualMachine(final VirtualMachine vm) { 35 | this.vm = vm; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/com/heliosapm/jvmti/agent/AgentMBean.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2016 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package com.heliosapm.jvmti.agent; 14 | 15 | import java.util.LinkedHashMap; 16 | import java.util.LongSummaryStatistics; 17 | 18 | /** 19 | *

Title: AgentMBean

20 | *

Description: JMX MBean interface for {@link Agent}

21 | *

Company: Helios Development Group LLC

22 | * @author Whitehead (nwhitehead AT heliosdev DOT org) 23 | *

com.heliosapm.jvmti.agent.AgentMBean

24 | */ 25 | 26 | public interface AgentMBean { 27 | /** The JMX ObjectName */ 28 | public static final String OBJECT_NAME = "com.heliosapm.jvmti:service=Agent"; 29 | 30 | /** 31 | * Returns the number of instances of the passed class 32 | * or any that implement or inherrit from it 33 | * @param className The name of the class to count instances for 34 | * @return the number of instances found 35 | */ 36 | public int getInstanceCountOfAny(final String className); 37 | 38 | /** 39 | * Returns the number of instances of the exact passed class 40 | * @param className The name of the class to count instances for 41 | * @return the number of instances found 42 | */ 43 | public int getInstanceCountOf(final String className); 44 | 45 | /** 46 | * Acquires all instances of the named class and invokes the js in the passed file 47 | * against each one. The output is captured and returned. 48 | * @param className The class name of instances to acquire and invoke against 49 | * @param fileName The file to read the js from 50 | * @param maxInstances The maximum number of instances to acquire 51 | * @return the output 52 | */ 53 | public String invokeAgainstInstancesOf(final String className, final String fileName, final int maxInstances); 54 | 55 | /** 56 | * Acquires all instances of the named class or any inherriting/implementing from it 57 | * and invokes the js in the passed file 58 | * against each one. The output is captured and returned. 59 | * @param className The class name of instances to acquire and invoke against 60 | * @param fileName The file to read the js from 61 | * @param maxInstances The maximum number of instances to acquire 62 | * @return the output 63 | */ 64 | public String invokeAgainstInstancesOfAny(final String className, final String fileName, final int maxInstances); 65 | 66 | /** 67 | * Returns the top N classes by count 68 | * @param className The name of the class to count instances for 69 | * @param n The top n value 70 | * @param excludePrims exclude primitives and arrays of primitives 71 | * @return A map of the number of class instances keyed by the class name 72 | */ 73 | public LinkedHashMap getTopNInstanceCounts(final String className, final int n, final boolean excludePrims); 74 | 75 | 76 | /** 77 | * Indicates if the agent was loaded at boot time or was attached 78 | * @return true if loaded, false if attached 79 | */ 80 | public boolean isAgentBootLoaded(); 81 | 82 | /** 83 | * Returns the location where the native library was loaded from 84 | * @return the library file name 85 | */ 86 | public String getNativeLibrary(); 87 | 88 | /** 89 | * Returns the total number of completed topN operations 90 | * @return the total number of completed topN operations 91 | * @see com.heliosapm.jvmti.util.TimerHistory#count() 92 | */ 93 | public long getTopNCount(); 94 | 95 | /** 96 | * Returns the average elapsed time of recent completed topN operations in ms. 97 | * @return the average elapsed time of recent completed topN operations 98 | * @see com.heliosapm.jvmti.util.TimerHistory#average() 99 | */ 100 | public double getTopNAverage(); 101 | 102 | /** 103 | * Returns the maximum elapsed time of recent completed topN operations in ms. 104 | * @return the maximum elapsed time of recent completed topN operations 105 | * @see com.heliosapm.jvmti.util.TimerHistory#max() 106 | */ 107 | public long getTopNMax(); 108 | 109 | /** 110 | * Returns the minimum elapsed time of recent completed topN operations in ms. 111 | * @return the minimum elapsed time of recent completed topN operations 112 | * @see com.heliosapm.jvmti.util.TimerHistory#min() 113 | */ 114 | public long getTopNMin(); 115 | 116 | /** 117 | * Returns the elapsed time pf the most recent topN operation in ms. 118 | * @return the elapsed time pf the most recent topN operation 119 | * @see com.heliosapm.jvmti.util.TimerHistory#min() 120 | */ 121 | public long getTopNLast(); 122 | 123 | /** 124 | * Returns combined statistics for recent topN operations 125 | * @return combined statistics for recent topN operations 126 | */ 127 | public LongSummaryStatistics getTopNStats(); 128 | 129 | /** 130 | * Resets the timers but not the counters 131 | */ 132 | public void resetTimers(); 133 | 134 | /** 135 | * Resets the timers and the counters 136 | */ 137 | public void resetTimersAll(); 138 | 139 | } 140 | -------------------------------------------------------------------------------- /src/main/java/com/heliosapm/jvmti/agent/EntryComparators.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2016 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package com.heliosapm.jvmti.agent; 14 | 15 | import java.util.Collections; 16 | import java.util.Comparator; 17 | import java.util.Map.Entry; 18 | import java.util.TreeSet; 19 | 20 | /** 21 | *

Title: EntryComparator

22 | *

Description: Comparator support for String/Integer map entry sets

23 | *

Company: Helios Development Group LLC

24 | * @author Whitehead (nwhitehead AT heliosdev DOT org) 25 | *

com.heliosapm.jvmti.agent.EntryComparator

26 | */ 27 | 28 | public class EntryComparators { 29 | /** Ascending Integer comparator */ 30 | public static final Comparator ASC_INT_COMP = new TreeSet().comparator(); 31 | /** Descending Integer comparator */ 32 | public static final Comparator DESC_INT_COMP = Collections.reverseOrder(ASC_INT_COMP); 33 | 34 | /** Ascending Long comparator */ 35 | public static final Comparator ASC_LONG_COMP = new TreeSet().comparator(); 36 | /** Descending Long comparator */ 37 | public static final Comparator DESC_LONG_COMP = Collections.reverseOrder(ASC_LONG_COMP); 38 | 39 | /** Descending long array comparator */ 40 | public static final Comparator DESC_LONGARR_COMP = new Comparator() { 41 | @Override 42 | public int compare(final long[] o1, final long[] o2) { 43 | return DESC_LONG_COMP.compare(o1[0], o2[0]); 44 | } 45 | }; 46 | 47 | 48 | /** Ascending String/Long entry set comparator */ 49 | public static final Comparator> ASC_ENTRY_STR_LONG_COMP = new AscendingLongEntryComparator(); 50 | /** Descending String/Long entry set comparator */ 51 | public static final Comparator> DESC_ENTRY_STR_LONG_COMP = new DescendingLongEntryComparator(); 52 | /** Ascending Class/Long entry set comparator */ 53 | public static final Comparator, Long>> ASC_ENTRY_CLASS_LONG_COMP = new AscendingLongEntryComparator>(); 54 | /** Descending String/Long entry set comparator */ 55 | public static final Comparator, Long>> DESC_ENTRY_CLASS_LONG_COMP = new DescendingLongEntryComparator>(); 56 | 57 | /** Wildcard key/long entry set comparator */ 58 | public static final Comparator> DESC_ENTRY_LONG_COMP = new WildcardDescendingLongEntryComparator(); 59 | 60 | /** Wildcard key/long[] entry set comparator */ 61 | public static final Comparator> DESC_ENTRY_LONGARR_COMP = new WildcardDescendingLongArrayEntryComparator(); 62 | 63 | 64 | 65 | 66 | /** Ascending String/Integer entry set comparator */ 67 | public static final Comparator> ASC_ENTRY_STR_INT_COMP = new AscendingEntryComparator(); 68 | /** Descending String/Integer entry set comparator */ 69 | public static final Comparator> DESC_ENTRY_STR_INT_COMP = new DescendingEntryComparator(); 70 | /** Ascending Class/Integer entry set comparator */ 71 | public static final Comparator, Integer>> ASC_ENTRY_CLASS_INT_COMP = new AscendingEntryComparator>(); 72 | /** Descending String/Integer entry set comparator */ 73 | public static final Comparator, Integer>> DESC_ENTRY_CLASS_INT_COMP = new DescendingEntryComparator>(); 74 | 75 | /** Ascending String/int[] entry set comparator */ 76 | public static final Comparator> ASC_ENTRY_STR_INTARR_COMP = new AscendingIntArrEntryComparator(); 77 | /** Descending String/int[] entry set comparator */ 78 | public static final Comparator> DESC_ENTRY_STR_INTARR_COMP = new DescendingIntArrEntryComparator(); 79 | /** Ascending Class/int[] entry set comparator */ 80 | public static final Comparator, int[]>> ASC_ENTRY_CLASS_INTARR_COMP = new AscendingIntArrEntryComparator>(); 81 | /** Descending String/int[] entry set comparator */ 82 | public static final Comparator, int[]>> DESC_ENTRY_CLASS_INTARR_COMP = new DescendingIntArrEntryComparator>(); 83 | 84 | 85 | private static class AscendingIntArrEntryComparator implements Comparator> { 86 | @Override 87 | public int compare(final Entry e1, final Entry e2) { 88 | return ASC_INT_COMP.compare(e1.getValue()[0], e2.getValue()[0]); 89 | } 90 | } 91 | 92 | private static class DescendingIntArrEntryComparator implements Comparator> { 93 | @Override 94 | public int compare(final Entry e1, final Entry e2) { 95 | return DESC_INT_COMP.compare(e1.getValue()[0], e2.getValue()[0]); 96 | } 97 | } 98 | 99 | 100 | private static class AscendingEntryComparator implements Comparator> { 101 | /** 102 | * {@inheritDoc} 103 | * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) 104 | */ 105 | @Override 106 | public int compare(final Entry e1, final Entry e2) { 107 | return ASC_INT_COMP.compare(e1.getValue(), e2.getValue()); 108 | } 109 | } 110 | 111 | private static class DescendingEntryComparator implements Comparator> { 112 | /** 113 | * {@inheritDoc} 114 | * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) 115 | */ 116 | @Override 117 | public int compare(final Entry e1, final Entry e2) { 118 | return DESC_INT_COMP.compare(e1.getValue(), e2.getValue()); 119 | } 120 | } 121 | 122 | private static class AscendingLongEntryComparator implements Comparator> { 123 | /** 124 | * {@inheritDoc} 125 | * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) 126 | */ 127 | @Override 128 | public int compare(final Entry e1, final Entry e2) { 129 | return ASC_LONG_COMP.compare(e1.getValue(), e2.getValue()); 130 | } 131 | } 132 | 133 | public static class DescendingLongEntryComparator implements Comparator> { 134 | /** 135 | * {@inheritDoc} 136 | * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) 137 | */ 138 | @Override 139 | public int compare(final Entry e1, final Entry e2) { 140 | return DESC_LONG_COMP.compare(e1.getValue(), e2.getValue()); 141 | } 142 | } 143 | 144 | private static class WildcardDescendingLongEntryComparator implements Comparator> { 145 | /** 146 | * {@inheritDoc} 147 | * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) 148 | */ 149 | @Override 150 | public int compare(final Entry e1, final Entry e2) { 151 | return DESC_LONG_COMP.compare(e1.getValue(), e2.getValue()); 152 | } 153 | } 154 | 155 | private static class WildcardDescendingLongArrayEntryComparator implements Comparator> { 156 | /** 157 | * {@inheritDoc} 158 | * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) 159 | */ 160 | @Override 161 | public int compare(final Entry e1, final Entry e2) { 162 | return DESC_LONGARR_COMP.compare(e1.getValue(), e2.getValue()); 163 | } 164 | } 165 | 166 | 167 | 168 | private EntryComparators(){} 169 | 170 | } 171 | -------------------------------------------------------------------------------- /src/main/java/com/heliosapm/jvmti/agent/VirtualMachineTask.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2016 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package com.heliosapm.jvmti.agent; 14 | 15 | import java.util.concurrent.Callable; 16 | 17 | import com.heliosapm.shorthand.attach.vm.VirtualMachine; 18 | 19 | /** 20 | *

Title: VirtualMachineTask

21 | *

Description: Defines a task to be run against a VirtualMachine instance

22 | *

Company: Helios Development Group LLC

23 | * @author Whitehead (nwhitehead AT heliosdev DOT org) 24 | *

com.heliosapm.jvmti.agent.VirtualMachineTask

25 | */ 26 | 27 | public interface VirtualMachineTask extends Callable { 28 | /** 29 | * Sets the VirtualMachine for the task to execute against 30 | * @param vm the VirtualMachine 31 | */ 32 | public void setVirtualMachine(VirtualMachine vm); 33 | 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/heliosapm/jvmti/extension/ExecutionScheduler.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.heliosapm.jvmti.extension; 5 | 6 | import java.lang.Thread.UncaughtExceptionHandler; 7 | import java.lang.management.ManagementFactory; 8 | import java.lang.reflect.Constructor; 9 | import java.util.concurrent.ArrayBlockingQueue; 10 | import java.util.concurrent.RejectedExecutionHandler; 11 | import java.util.concurrent.ScheduledFuture; 12 | import java.util.concurrent.ScheduledThreadPoolExecutor; 13 | import java.util.concurrent.ThreadFactory; 14 | import java.util.concurrent.ThreadPoolExecutor; 15 | import java.util.concurrent.TimeUnit; 16 | import java.util.concurrent.atomic.AtomicInteger; 17 | 18 | import javax.management.ObjectName; 19 | 20 | import org.pmw.tinylog.Logger; 21 | 22 | import com.codahale.metrics.JmxReporter; 23 | import com.codahale.metrics.MetricRegistry; 24 | import com.heliosapm.jvmti.agent.NativeAgent; 25 | import com.heliosapm.utils.jmx.JMXHelper; 26 | 27 | import io.micrometer.core.instrument.Clock; 28 | import io.micrometer.core.instrument.Metrics; 29 | import io.micrometer.core.instrument.dropwizard.DropwizardMeterRegistry; 30 | import io.micrometer.core.instrument.util.HierarchicalNameMapper; 31 | 32 | /** 33 | * @author nwhitehead 34 | * 35 | */ 36 | public class ExecutionScheduler implements ExecutionSchedulerMXBean, UncaughtExceptionHandler, RejectedExecutionHandler { 37 | private static volatile ExecutionScheduler instance = null; 38 | private static final Object lock = new Object(); 39 | public static final int CORES = ManagementFactory.getOperatingSystemMXBean().getAvailableProcessors(); 40 | private final DropwizardMeterRegistry promReg = new DropwizardMeterRegistry(HierarchicalNameMapper.DEFAULT, Clock.SYSTEM); 41 | private final MetricRegistry registry = promReg.getDropwizardRegistry(); 42 | 43 | private final ScheduledThreadPoolExecutor scheduler; 44 | private final ThreadPoolExecutor executor; 45 | 46 | private final ObjectName objectName = JMXHelper.objectName("com.heliosapm.jvmti:service=ExecutionScheduler"); 47 | 48 | public static ExecutionScheduler getInstance() { 49 | if(instance==null) { 50 | synchronized(lock) { 51 | if(instance==null) { 52 | instance = new ExecutionScheduler(); 53 | } 54 | } 55 | } 56 | return instance; 57 | } 58 | 59 | private ExecutionScheduler() { 60 | Metrics.globalRegistry.add(promReg); 61 | executor = new ThreadPoolExecutor(1, CORES, 60, TimeUnit.SECONDS, new ArrayBlockingQueue(32), new ThreadFactory(){ 62 | final AtomicInteger serial = new AtomicInteger(0); 63 | @Override 64 | public Thread newThread(Runnable r) { 65 | Thread t = new Thread(r, "ExecutorThread#" + serial.incrementAndGet()); 66 | t.setDaemon(true); 67 | return t; 68 | } 69 | }, this); 70 | scheduler = new ScheduledThreadPoolExecutor(2, new ThreadFactory(){ 71 | final AtomicInteger serial = new AtomicInteger(0); 72 | @Override 73 | public Thread newThread(Runnable r) { 74 | Thread t = new Thread(r, "SchedulerThread#" + serial.incrementAndGet()); 75 | t.setDaemon(true); 76 | return t; 77 | } 78 | }){ 79 | // @Override 80 | // public ScheduledFuture scheduleWithFixedDelay(final Runnable command, long initialDelay, long delay, TimeUnit unit) { 81 | // return super.scheduleWithFixedDelay(new Runnable(){ 82 | // public void run() { 83 | // executor.execute(command); 84 | // } 85 | // }, initialDelay, delay, unit); 86 | // } 87 | }; 88 | JmxReporter.forRegistry(registry).build().start(); 89 | JMXHelper.registerMBean(this, objectName); 90 | Logger.info("ExecutionScheduler Started"); 91 | } 92 | 93 | public void schedule(String className) { 94 | try { 95 | @SuppressWarnings("unchecked") 96 | Class clazz = (Class) Class.forName(className); 97 | Constructor ctor = clazz.getDeclaredConstructor(MetricRegistry.class, NativeAgent.class); 98 | ScheduledExtension se = ctor.newInstance(registry, NativeAgent.getInstance()); 99 | long fixedDelay = se.getFixedDelay(); 100 | long initialDelay = se.getInitialDelay(); 101 | if(fixedDelay > 0) { 102 | ScheduledFuture handle = scheduler.scheduleWithFixedDelay(new Runnable(){ 103 | public void run() { 104 | executor.execute(se); 105 | } 106 | }, initialDelay, fixedDelay, TimeUnit.MILLISECONDS); 107 | se.setScheduleHandle(handle); 108 | Logger.info("Extension [{}] scheduled for repeated execution every {} ms.", className, fixedDelay); 109 | } else { 110 | Logger.info("Extension [{}] had no schedule", className); 111 | } 112 | } catch (Exception ex) { 113 | Logger.error("Failed to schedule extension: {}", className, ex); 114 | ex.printStackTrace(System.err); 115 | } 116 | } 117 | 118 | @Override 119 | public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { 120 | Logger.error("Rejected Execution: {}, Qcap: {}", r, executor.getQueue().remainingCapacity()); 121 | } 122 | 123 | @Override 124 | public void uncaughtException(Thread t, Throwable e) { 125 | Logger.error("Uncaught exception on thread {}", t, e); 126 | } 127 | 128 | /** 129 | * @return the registry 130 | */ 131 | public MetricRegistry getRegistry() { 132 | return registry; 133 | } 134 | 135 | /** 136 | * @return 137 | * @see java.util.concurrent.ThreadPoolExecutor#getCorePoolSize() 138 | */ 139 | public int getCorePoolSize() { 140 | return executor.getCorePoolSize(); 141 | } 142 | 143 | /** 144 | * @return 145 | * @see java.util.concurrent.ThreadPoolExecutor#getMaximumPoolSize() 146 | */ 147 | public int getMaximumPoolSize() { 148 | return executor.getMaximumPoolSize(); 149 | } 150 | 151 | /** 152 | * @return 153 | * @see java.util.concurrent.ThreadPoolExecutor#getQueue() 154 | */ 155 | public int getQueueDepth() { 156 | return executor.getQueue().size(); 157 | } 158 | 159 | public int getQueueAvailCap() { 160 | return executor.getQueue().remainingCapacity(); 161 | } 162 | 163 | 164 | /** 165 | * 166 | * @see java.util.concurrent.ThreadPoolExecutor#purge() 167 | */ 168 | public void purge() { 169 | executor.purge(); 170 | } 171 | 172 | /** 173 | * @return 174 | * @see java.util.concurrent.ThreadPoolExecutor#getPoolSize() 175 | */ 176 | public int getPoolSize() { 177 | return executor.getPoolSize(); 178 | } 179 | 180 | /** 181 | * @return 182 | * @see java.util.concurrent.ThreadPoolExecutor#getActiveCount() 183 | */ 184 | public int getActiveCount() { 185 | return executor.getActiveCount(); 186 | } 187 | 188 | /** 189 | * @return 190 | * @see java.util.concurrent.ThreadPoolExecutor#getLargestPoolSize() 191 | */ 192 | public int getLargestPoolSize() { 193 | return executor.getLargestPoolSize(); 194 | } 195 | 196 | /** 197 | * @return 198 | * @see java.util.concurrent.ThreadPoolExecutor#getTaskCount() 199 | */ 200 | public long getTaskCount() { 201 | return executor.getTaskCount(); 202 | } 203 | 204 | /** 205 | * @return 206 | * @see java.util.concurrent.ThreadPoolExecutor#getCompletedTaskCount() 207 | */ 208 | public long getCompletedTaskCount() { 209 | return executor.getCompletedTaskCount(); 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /src/main/java/com/heliosapm/jvmti/extension/ExecutionSchedulerMXBean.java: -------------------------------------------------------------------------------- 1 | package com.heliosapm.jvmti.extension; 2 | 3 | public interface ExecutionSchedulerMXBean { 4 | 5 | public int getCorePoolSize(); 6 | 7 | /** 8 | * @return 9 | * @see java.util.concurrent.ThreadPoolExecutor#getMaximumPoolSize() 10 | */ 11 | public int getMaximumPoolSize(); 12 | 13 | /** 14 | * @return 15 | * @see java.util.concurrent.ThreadPoolExecutor#getQueue() 16 | */ 17 | public int getQueueDepth(); 18 | 19 | public int getQueueAvailCap(); 20 | 21 | 22 | /** 23 | * 24 | * @see java.util.concurrent.ThreadPoolExecutor#purge() 25 | */ 26 | public void purge(); 27 | 28 | /** 29 | * @return 30 | * @see java.util.concurrent.ThreadPoolExecutor#getPoolSize() 31 | */ 32 | public int getPoolSize(); 33 | 34 | /** 35 | * @return 36 | * @see java.util.concurrent.ThreadPoolExecutor#getActiveCount() 37 | */ 38 | public int getActiveCount(); 39 | 40 | /** 41 | * @return 42 | * @see java.util.concurrent.ThreadPoolExecutor#getLargestPoolSize() 43 | */ 44 | public int getLargestPoolSize(); 45 | 46 | /** 47 | * @return 48 | * @see java.util.concurrent.ThreadPoolExecutor#getTaskCount() 49 | */ 50 | public long getTaskCount(); 51 | 52 | /** 53 | * @return 54 | * @see java.util.concurrent.ThreadPoolExecutor#getCompletedTaskCount() 55 | */ 56 | public long getCompletedTaskCount(); 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/heliosapm/jvmti/extension/Scheduled.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.heliosapm.jvmti.extension; 5 | 6 | import java.lang.annotation.Documented; 7 | import java.lang.annotation.ElementType; 8 | import java.lang.annotation.Retention; 9 | import java.lang.annotation.RetentionPolicy; 10 | import java.lang.annotation.Target; 11 | 12 | @Target({ElementType.TYPE}) 13 | @Retention(RetentionPolicy.RUNTIME) 14 | @Documented 15 | public @interface Scheduled { 16 | 17 | 18 | /** 19 | * Execute the annotated method with a fixed period in milliseconds between the 20 | * end of the last invocation and the start of the next. 21 | * @return the delay in milliseconds 22 | */ 23 | long fixedDelay() default -1; 24 | 25 | 26 | /** 27 | * Number of milliseconds to delay before the first execution of a 28 | * {@link #fixedRate()} or {@link #fixedDelay()} task. 29 | * @return the initial delay in milliseconds 30 | * @since 3.2 31 | */ 32 | long initialDelay() default 0; 33 | 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/heliosapm/jvmti/extension/ScheduledExtension.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.heliosapm.jvmti.extension; 5 | 6 | import java.util.concurrent.ScheduledFuture; 7 | 8 | import org.pmw.tinylog.Logger; 9 | 10 | import com.codahale.metrics.Counter; 11 | import com.codahale.metrics.MetricRegistry; 12 | import com.codahale.metrics.Timer; 13 | import com.codahale.metrics.Timer.Context; 14 | import com.heliosapm.jvmti.agent.NativeAgent; 15 | 16 | /** 17 | * @author nwhitehead 18 | * 19 | */ 20 | public abstract class ScheduledExtension implements Runnable { 21 | 22 | protected ScheduledFuture scheduleHandle = null; 23 | protected final MetricRegistry metricRegistry; 24 | protected final NativeAgent nativeAgent; 25 | protected final Timer runTimer; 26 | protected final Counter runErrors; 27 | protected final long fixedDelay; 28 | protected final long initialDelay; 29 | 30 | 31 | 32 | public abstract void doRun() throws Exception; 33 | 34 | protected ScheduledExtension(final MetricRegistry metricRegistry, final NativeAgent nativeAgent) { 35 | this.metricRegistry = metricRegistry; 36 | this.nativeAgent = nativeAgent; 37 | runTimer = metricRegistry.timer(MetricRegistry.name(getClass(), "timer")); 38 | runErrors = metricRegistry.counter(MetricRegistry.name(getClass(), "errors")); 39 | Scheduled scheduled = getClass().getAnnotation(Scheduled.class); 40 | if(scheduled!=null && scheduled.fixedDelay()!=-1) { 41 | fixedDelay = scheduled.fixedDelay(); 42 | initialDelay = scheduled.initialDelay(); 43 | } else { 44 | fixedDelay = -1; 45 | initialDelay = -1; 46 | } 47 | } 48 | 49 | public final void run() { 50 | Context ctx = runTimer.time(); 51 | try { 52 | doRun(); 53 | ctx.close(); 54 | } catch (Exception ex) { 55 | runErrors.inc(); 56 | Logger.warn("ScheduledExtension execution failure", ex); 57 | ex.printStackTrace(System.err); 58 | } 59 | } 60 | 61 | /** 62 | * @return the fixedDelay 63 | */ 64 | public long getFixedDelay() { 65 | return fixedDelay; 66 | } 67 | 68 | /** 69 | * @return the initialDelay 70 | */ 71 | public long getInitialDelay() { 72 | return initialDelay; 73 | } 74 | 75 | /** 76 | * @return the scheduleHandle 77 | */ 78 | public ScheduledFuture getScheduleHandle() { 79 | return scheduleHandle; 80 | } 81 | 82 | /** 83 | * @param scheduleHandle the scheduleHandle to set 84 | */ 85 | public void setScheduleHandle(ScheduledFuture scheduleHandle) { 86 | this.scheduleHandle = scheduleHandle; 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /src/main/java/com/heliosapm/jvmti/extension/impls/DirectByteBufferAllocations.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.heliosapm.jvmti.extension.impls; 5 | 6 | import java.nio.ByteBuffer; 7 | 8 | import org.pmw.tinylog.Logger; 9 | 10 | import com.codahale.metrics.Gauge; 11 | import com.codahale.metrics.MetricRegistry; 12 | import com.heliosapm.jvmti.agent.NativeAgent; 13 | import com.heliosapm.jvmti.extension.Scheduled; 14 | import com.heliosapm.jvmti.extension.ScheduledExtension; 15 | 16 | /** 17 | * @author nwhitehead 18 | * 19 | */ 20 | @Scheduled(fixedDelay=5000, initialDelay=1000) 21 | public class DirectByteBufferAllocations extends ScheduledExtension { 22 | private static final String DBB_NAME = "java.nio.DirectByteBuffer"; 23 | private static volatile Class clazz = null; 24 | private final long[] instanceCount = new long[]{0}; 25 | private final long[] totalAllocated = new long[]{0}; 26 | 27 | private final Gauge instanceCountGauge = new Gauge() { 28 | @Override 29 | public Long getValue() { 30 | return instanceCount[0]; 31 | } 32 | }; 33 | private final Gauge totalAllocatedGauge = new Gauge() { 34 | @Override 35 | public Long getValue() { 36 | return totalAllocated[0]; 37 | } 38 | }; 39 | 40 | /** 41 | * @param metricRegistry 42 | * @param nativeAgent 43 | */ 44 | public DirectByteBufferAllocations(MetricRegistry metricRegistry, NativeAgent nativeAgent) { 45 | super(metricRegistry, nativeAgent); 46 | metricRegistry.register(MetricRegistry.name(getClass(), "instance.count"), instanceCountGauge); 47 | metricRegistry.register(MetricRegistry.name(getClass(), "instance.allocation"), totalAllocatedGauge); 48 | } 49 | 50 | /** 51 | * 52 | * @see com.heliosapm.jvmti.extension.ScheduledExtension#doRun() 53 | */ 54 | @SuppressWarnings("unchecked") 55 | @Override 56 | public void doRun() throws Exception { 57 | if(clazz==null) { 58 | try { 59 | clazz = (Class) Class.forName(DBB_NAME); 60 | } catch (Exception ex) { 61 | Logger.error("Failed to load class [{}]. Stopping scheduled execution.", DBB_NAME, ex); 62 | if(scheduleHandle != null) { 63 | scheduleHandle.cancel(true); 64 | } 65 | return; 66 | } 67 | } 68 | final long[] iCount = new long[]{0}; 69 | final long[] tAllocated = new long[]{0}; 70 | nativeAgent.instancesOf(clazz, Integer.MAX_VALUE, buff -> { 71 | iCount[0]++; 72 | tAllocated[0] += buff.capacity(); 73 | }); 74 | instanceCount[0] = iCount[0]; 75 | totalAllocated[0] = tAllocated[0]; 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/com/heliosapm/jvmti/extension/impls/HotspotExtension.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.heliosapm.jvmti.extension.impls; 5 | 6 | import javax.management.ObjectName; 7 | 8 | import org.pmw.tinylog.Logger; 9 | 10 | import com.codahale.metrics.JmxAttributeGauge; 11 | import com.codahale.metrics.MetricRegistry; 12 | import com.heliosapm.jvmti.agent.NativeAgent; 13 | import com.heliosapm.jvmti.extension.Scheduled; 14 | import com.heliosapm.jvmti.extension.ScheduledExtension; 15 | import com.heliosapm.jvmti.metrics.LongGauge; 16 | import com.heliosapm.utils.jmx.JMXHelper; 17 | 18 | /** 19 | * @author nwhitehead 20 | * 21 | */ 22 | @Scheduled(fixedDelay=5000, initialDelay=1000) 23 | public class HotspotExtension extends ScheduledExtension { 24 | final boolean enabled; 25 | final ObjectName hotspotRuntime = JMXHelper.objectName("sun.management:type=HotspotRuntime"); 26 | final LongGauge safepointCount = new LongGauge(); 27 | final LongGauge safepointSyncTime = new LongGauge(); 28 | final LongGauge totalSafepointTime = new LongGauge(); 29 | /** 30 | * @param metricRegistry 31 | * @param nativeAgent 32 | */ 33 | public HotspotExtension(MetricRegistry metricRegistry, NativeAgent nativeAgent) { 34 | super(metricRegistry, nativeAgent); 35 | enabled = JMXHelper.registerHotspotInternal(); 36 | if(enabled) { 37 | metricRegistry.register(MetricRegistry.name(getClass(), "runtime.safepoint.count"), safepointCount); 38 | metricRegistry.register(MetricRegistry.name(getClass(), "runtime.safepoint.synctime"), safepointSyncTime); 39 | metricRegistry.register(MetricRegistry.name(getClass(), "runtime.safepoint.totaltime"), totalSafepointTime); 40 | } 41 | } 42 | 43 | /** 44 | * sun.management:type=HotspotRuntime 45 | * SafepointCount 46 | * SafepointSyncTime 47 | * TotalSafepointTime 48 | * 49 | * @see com.heliosapm.jvmti.extension.ScheduledExtension#doRun() 50 | */ 51 | @Override 52 | public void doRun() throws Exception { 53 | if(!enabled) { 54 | Logger.info("HotspotExtension failed to register. Cancelling schedule"); 55 | if(scheduleHandle!=null) { 56 | scheduleHandle.cancel(true); 57 | } 58 | return; 59 | } 60 | safepointCount.update(JMXHelper.getAttribute(hotspotRuntime, "SafepointCount")); 61 | safepointSyncTime.update(JMXHelper.getAttribute(hotspotRuntime, "SafepointSyncTime")); 62 | totalSafepointTime.update(JMXHelper.getAttribute(hotspotRuntime, "TotalSafepointTime")); 63 | 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/com/heliosapm/jvmti/extension/impls/thread/ThreadPoolMonitor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.heliosapm.jvmti.extension.impls.thread; 5 | 6 | import java.lang.instrument.ClassFileTransformer; 7 | import java.lang.instrument.IllegalClassFormatException; 8 | import java.lang.instrument.Instrumentation; 9 | import java.security.ProtectionDomain; 10 | import java.util.concurrent.ThreadPoolExecutor; 11 | import java.util.concurrent.atomic.AtomicBoolean; 12 | 13 | import org.pmw.tinylog.Logger; 14 | 15 | import com.codahale.metrics.MetricRegistry; 16 | import com.heliosapm.jvmti.agent.NativeAgent; 17 | import com.heliosapm.jvmti.extension.ScheduledExtension; 18 | import com.heliosapm.jvmti.install.JavaAgent2; 19 | import com.heliosapm.utils.unsafe.UnsafeAdapter; 20 | 21 | import javassist.ClassPool; 22 | import javassist.CtClass; 23 | import javassist.CtMethod; 24 | 25 | /** 26 | * @author nwhitehead 27 | * 28 | */ 29 | public class ThreadPoolMonitor extends ScheduledExtension { 30 | 31 | private static final AtomicBoolean TPOOLS_INSTRUMENTED = new AtomicBoolean(false); 32 | 33 | public ThreadPoolMonitor(MetricRegistry metricRegistry, NativeAgent nativeAgent) { 34 | super(metricRegistry, nativeAgent); 35 | instrumentThreadPools(); 36 | } 37 | 38 | @Override 39 | public void doRun() throws Exception { 40 | // TODO Auto-generated method stub 41 | 42 | } 43 | 44 | 45 | public static void beforeExecute(Thread t, Runnable r) { 46 | Logger.info("BEFORE EXEC [{}]: {}, {}", Thread.currentThread(), t, r); 47 | } 48 | 49 | public static void afterExecute(Runnable r, Throwable t) { 50 | Logger.info("AFTER EXEC [{}]: {}, {}", Thread.currentThread(), r, t); 51 | } 52 | 53 | private static void instrumentThreadPools() { 54 | final Instrumentation instr = JavaAgent2.INSTRUMENTATION; 55 | if(instr==null) return; 56 | if(TPOOLS_INSTRUMENTED.compareAndSet(false, true)) { 57 | try { 58 | 59 | ClassPool cp = new ClassPool(); 60 | cp.appendSystemPath(); 61 | CtClass threadCtClass = cp.get(Thread.class.getName()); 62 | CtClass runnableCtClass = cp.get(Runnable.class.getName()); 63 | CtClass throwableCtClass = cp.get(Runnable.class.getName()); 64 | CtClass ME = cp.get("com.heliosapm.jvmti.extension.impls.thread.ThreadPoolMonitor"); 65 | CtClass threadPoolCtClass = cp.get(ThreadPoolExecutor.class.getName()); 66 | CtMethod before = threadPoolCtClass.getDeclaredMethod("beforeExecute"); 67 | CtMethod after = threadPoolCtClass.getDeclaredMethod("afterExecute"); 68 | before.insertBefore("{com.heliosapm.jvmti.extension.impls.thread.ThreadPoolMonitor.beforeExecute($1, $2);}"); 69 | after.insertBefore("{com.heliosapm.jvmti.extension.impls.thread.ThreadPoolMonitor.afterExecute($1, $2);}"); 70 | final byte[] byteCode = threadPoolCtClass.toBytecode(); 71 | final byte[] meCode = ME.toBytecode(); 72 | 73 | final String clazzName = ThreadPoolExecutor.class.getName().replace('.', '/'); 74 | final String myClazzName = "com.heliosapm.jvmti.extension.impls.thread.ThreadPoolMonitor".replace('.', '/'); 75 | final ProtectionDomain pd = ThreadPoolExecutor.class.getProtectionDomain(); 76 | final ClassFileTransformer transformer = new ClassFileTransformer() { 77 | @Override 78 | public byte[] transform(ClassLoader loader, String className, Class classBeingRedefined, 79 | ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException { 80 | if(clazzName.equals(className)) { 81 | UnsafeAdapter.defineClass("com.heliosapm.jvmti.extension.impls.thread.ThreadPoolMonitor", meCode, 0, meCode.length, loader, pd); 82 | 83 | return byteCode; 84 | } 85 | return classfileBuffer; 86 | } 87 | }; 88 | try { 89 | instr.addTransformer(transformer, true); 90 | instr.retransformClasses(ThreadPoolExecutor.class); 91 | Logger.info("Instrumented ThreadPools"); 92 | } finally { 93 | instr.removeTransformer(transformer); 94 | } 95 | 96 | } catch (Exception ex) { 97 | Logger.error("Failed to instrument ThreadPools", ex); 98 | ex.printStackTrace(System.err); 99 | TPOOLS_INSTRUMENTED.set(false); 100 | } 101 | } 102 | 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /src/main/java/com/heliosapm/jvmti/install/AgentInstaller.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2016 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package com.heliosapm.jvmti.install; 14 | 15 | import java.util.Map; 16 | import java.util.logging.Level; 17 | import java.util.logging.Logger; 18 | 19 | import com.heliosapm.shorthand.attach.vm.VirtualMachine; 20 | 21 | //import com.heliosapm.utils.classload.IsolatedClassLoader; 22 | 23 | /** 24 | *

Title: AgentInstaller

25 | *

Description: Installs the OIF agent in the target JVM

26 | *

Company: Helios Development Group LLC

27 | * @author Whitehead (nwhitehead AT heliosdev DOT org) 28 | *

com.heliosapm.jvmti.install.AgentInstaller

29 | */ 30 | 31 | public class AgentInstaller { 32 | /** Static class logger */ 33 | private final static Logger LOG = Logger.getLogger(AgentInstaller.class.getName()); 34 | 35 | /** The args delimeter terminator */ 36 | public static final String DELIM_TERM = "##"; 37 | /** The booted agent instance */ 38 | public static Object bootedAgent = null; 39 | 40 | /** System property set in target JVM when agent is installed */ 41 | public static final String AGENT_INSTALLED_PROP = "com.heliosapm.jvmti.agent.installed"; 42 | 43 | /** 44 | * Entry point to invoke the agent installer 45 | * @param args The installer directives:
    46 | *
  • --pid <jvm id> The target JVM to install into
  • 47 | *
  • --D <key>=<value> Specifies a system property to set before launching the agent. 48 | * Can be specified multiple times.
  • 49 | *
50 | */ 51 | public static void main(final String[] args) { 52 | final StringBuilder packedAgentOptions = new StringBuilder(); 53 | final Map agentOptions = AgentOption.commandLine(packedAgentOptions, args); 54 | install(agentOptions, packedAgentOptions.toString()); 55 | } 56 | 57 | private static void install(final Map agentOptions, final String packedAgentOptions) { 58 | VirtualMachine vm = null; 59 | try { 60 | final String pid = (String)agentOptions.get(AgentOption.PID); 61 | LOG.log(Level.INFO, "Installing Agent into JVM [" + pid + "]..."); 62 | vm = VirtualMachine.attach(pid); 63 | if(vm.getSystemProperties().containsKey(AGENT_INSTALLED_PROP)) { 64 | LOG.log(Level.WARNING, "Agent already installed in JVM [" + pid + "]"); 65 | return; 66 | } 67 | final String jarFile = AgentInstaller.class.getProtectionDomain().getCodeSource().getLocation().getFile(); 68 | LOG.log(Level.INFO, "Agent jar [" + jarFile + "]"); 69 | if(packedAgentOptions.isEmpty()) { 70 | LOG.log(Level.INFO, "Executing [vm.loadAgent(\"" + jarFile + "\")]"); 71 | vm.loadAgent(jarFile); 72 | } else { 73 | LOG.log(Level.INFO, "Executing [vm.loadAgent(\"" + jarFile + "\",\"" + packedAgentOptions + "\")]"); 74 | vm.loadAgent(jarFile, packedAgentOptions); 75 | } 76 | LOG.log(Level.INFO, "Successfully installed Agent jar [" + jarFile + "] into JVM [" + pid + "]"); 77 | } catch (Throwable ex) { 78 | LOG.log(Level.SEVERE, "Failed to install Agent", ex); 79 | } finally { 80 | if(vm!=null) try { vm.detach(); } catch (Exception x) {/* No Op */} 81 | } 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/com/heliosapm/jvmti/install/AgentOption.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2016 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package com.heliosapm.jvmti.install; 14 | 15 | import java.io.File; 16 | import java.net.URL; 17 | import java.util.Collections; 18 | import java.util.EnumMap; 19 | import java.util.EnumSet; 20 | import java.util.LinkedHashSet; 21 | import java.util.Map; 22 | import java.util.Properties; 23 | import java.util.Set; 24 | import java.util.regex.Pattern; 25 | 26 | /** 27 | *

Title: AgentOption

28 | *

Description: Enumeration of agent options

29 | *

Company: Helios Development Group LLC

30 | * @author Whitehead (nwhitehead AT heliosdev DOT org) 31 | *

com.heliosapm.jvmti.install.AgentOption

32 | */ 33 | 34 | public enum AgentOption implements AgentOptionProcessor { 35 | /** Additional classpath entry for agent */ 36 | CP(true, true, false, false, false) { 37 | @Override 38 | public void agentOpts(String value, Map extracted) { 39 | URL url; 40 | try { 41 | url = new URL(value); 42 | final File f = new File(url.getFile()); 43 | if(!f.exists()) throw new IllegalArgumentException("Invalid classpath URL (file): [" + f + "]"); 44 | url = f.getAbsoluteFile().toURI().toURL(); 45 | } catch (Exception ex) { 46 | throw new IllegalArgumentException("Invalid classpath URL: [" + value + "]", ex); 47 | } 48 | @SuppressWarnings("unchecked") 49 | Set urls = (Set)extracted.get(this); 50 | if(urls==null) { 51 | urls = new LinkedHashSet(); 52 | extracted.put(this, urls); 53 | } 54 | urls.add(url); 55 | } 56 | @Override 57 | public void commandLine(final String value, final StringBuilder agentOpts, final Map extracted) { 58 | final URL url; 59 | try { 60 | url = new URL(value); 61 | } catch (Exception ex) { 62 | throw new IllegalArgumentException("Invalid classpath URL: [" + value + "]", ex); 63 | } 64 | if(agentOpts.length()!=0) { 65 | agentOpts.append(AgentInstaller.DELIM_TERM); 66 | } 67 | agentOpts.append(name()).append(":").append(url.toString()); 68 | } 69 | }, 70 | /** The process ID of the JVM to install to */ 71 | PID(false, false, false, true, false){ 72 | @Override 73 | public void agentOpts(final String value, final Map extracted) { 74 | /* No Op */ 75 | } 76 | @Override 77 | public void commandLine(final String value, final StringBuilder agentOpts, final Map extracted) { 78 | if(extracted.containsKey(this)) throw new IllegalArgumentException("Multiple PID arguments"); 79 | extracted.put(this, value); 80 | } 81 | }, 82 | /** A system property to set in the install target */ 83 | D(true, true, false, false, false){ 84 | @Override 85 | public void agentOpts(final String value, final Map extracted) { 86 | final String[] sysProp = NAME_VALUE_SPLITTER.split(value); 87 | if(sysProp.length==2) { 88 | if(sysProp[0]==null || sysProp[0].trim().isEmpty() || sysProp[1]==null || sysProp[1].trim().isEmpty()) { 89 | throw new RuntimeException("Invalid system property definition: [" + value + "]"); 90 | } 91 | Properties p = (Properties) extracted.get(this); 92 | if(p==null) { 93 | p = new Properties(); 94 | extracted.put(this, p); 95 | } 96 | p.setProperty(sysProp[0].trim(), sysProp[1].trim()); 97 | } else { 98 | throw new RuntimeException("Invalid system property definition: [" + value + "]"); 99 | } 100 | } 101 | @Override 102 | public void commandLine(final String value, final StringBuilder agentOpts, final Map extracted) { 103 | if(agentOpts.length()!=0) { 104 | agentOpts.append(AgentInstaller.DELIM_TERM); 105 | } 106 | agentOpts.append(name()).append(":").append(value); 107 | } 108 | }; 109 | 110 | private AgentOption(final boolean dupsAllowed, final boolean passedToTarget, final boolean flag, final boolean mandatoryCl, final boolean mandatoryOpt) { 111 | this.dupsAllowed = dupsAllowed; 112 | this.passedToTarget = passedToTarget; 113 | this.flag = flag; 114 | this.mandatoryCl = mandatoryCl; 115 | this.mandatoryOpt = mandatoryOpt; 116 | } 117 | 118 | /** Indicates if this option is allowed to be supplied more than once */ 119 | public final boolean dupsAllowed; 120 | /** Indicates if this command line option is relayed to the target JVM as an agent option */ 121 | public final boolean passedToTarget; 122 | /** Indicates if this command line option is a flag with no associated value */ 123 | public final boolean flag; 124 | /** Indicates if this command line option is mandatory */ 125 | public final boolean mandatoryCl; 126 | /** Indicates if this agent option is mandatory */ 127 | public final boolean mandatoryOpt; 128 | 129 | private static final AgentOption[] values = values(); 130 | 131 | /** The packed agent options splitter */ 132 | public static final Pattern PACKED_SPLITTER = Pattern.compile(AgentInstaller.DELIM_TERM); 133 | /** Options that are mandatory on the command line */ 134 | public static final Set mandatoryCommandLine; 135 | /** Options that are mandatory in the agent options */ 136 | public static final Set mandatoryOption; 137 | 138 | static { 139 | final Set tmpCl = EnumSet.noneOf(AgentOption.class); 140 | final Set tmpOpt = EnumSet.noneOf(AgentOption.class); 141 | for(AgentOption ao: values) { 142 | if(ao.mandatoryCl) tmpCl.add(ao); 143 | } 144 | mandatoryCommandLine = Collections.unmodifiableSet(tmpCl); 145 | mandatoryOption = Collections.unmodifiableSet(tmpOpt); 146 | } 147 | 148 | private static Set mandatoryCl() { 149 | return EnumSet.copyOf(mandatoryCommandLine); 150 | } 151 | 152 | private static Set mandatoryOpt() { 153 | // we have none for now 154 | //return EnumSet.copyOf(mandatoryOption); 155 | return EnumSet.noneOf(AgentOption.class); 156 | } 157 | 158 | 159 | /** 160 | * Decodes the passed string to an agent option 161 | * @param code The code to decode 162 | * @return The decoded AgentOption 163 | */ 164 | public static AgentOption decode(final String code) { 165 | if(code==null || code.trim().isEmpty()) throw new IllegalArgumentException("The passed code was null"); 166 | final String _code = code.trim().toUpperCase(); 167 | try { 168 | return valueOf(_code); 169 | } catch (Exception ex) { 170 | throw new IllegalArgumentException("Invalid AgentOption [" + code + "]"); 171 | } 172 | } 173 | 174 | /** 175 | * Handles agent option processing 176 | * @param packedAgentOptions The string passed to the agent containing the options to appy 177 | * @return A map of actionable options extracted from the packed options 178 | */ 179 | public static Map agentOptions(final String packedAgentOptions) { 180 | final Set mandatory = mandatoryOpt(); 181 | final Map map = new EnumMap(AgentOption.class); 182 | if(packedAgentOptions!=null && !packedAgentOptions.trim().isEmpty()) { 183 | final String[] options = PACKED_SPLITTER.split(packedAgentOptions); 184 | for(String option: options) { 185 | final int index = option.indexOf(':'); 186 | if(index==-1) throw new IllegalArgumentException("Invalid unpacked agent option [" + option + "]"); 187 | final String agentOption = option.substring(0, index); 188 | final AgentOption ao = decode(agentOption); 189 | final String optionValue; 190 | if(ao.flag) { 191 | optionValue = null; 192 | } else { 193 | optionValue = option.substring(index+1); 194 | if(optionValue.isEmpty()) throw new IllegalArgumentException("Empty unpacked agent option value [" + option + "]"); 195 | } 196 | ao.agentOpts(optionValue, map); 197 | mandatory.remove(ao); 198 | } 199 | } 200 | if(!mandatory.isEmpty()) { 201 | throw new RuntimeException("Missing mandatory agent option[s]:" + mandatory); 202 | } 203 | 204 | return map; 205 | } 206 | 207 | /** 208 | * Handles command line processing 209 | * @param allAgentOptions A buffer that collects all command line options intended to 210 | * be relayed to a jmv installation as agent options 211 | * @param commandLine The command line options to parse 212 | * @return A map of actionable options extracted from the command line 213 | */ 214 | public static Map commandLine(final StringBuilder allAgentOptions, final String...commandLine) { 215 | final Set mandatory = mandatoryCl(); 216 | final int maxIndex = commandLine.length-1; 217 | final Map map = new EnumMap(AgentOption.class); 218 | for(int i = 0; i < commandLine.length; i++) { 219 | if(commandLine[i].indexOf("--")==0) { 220 | final String agentOption; 221 | final String optionValue; 222 | final AgentOption ao; 223 | String v = commandLine[i].substring(2); 224 | final int index = v.indexOf('='); 225 | if(index==-1) { 226 | //============================================= 227 | // option is flag, or value is next arg 228 | //============================================= 229 | agentOption = v; 230 | ao = decode(agentOption); 231 | if(!ao.flag) { 232 | i++; 233 | if(i > maxIndex) { 234 | throw new RuntimeException("No value provided for option [" + ao.name() + "]"); 235 | } 236 | optionValue = commandLine[i]; 237 | } else { 238 | optionValue = null; 239 | } 240 | } else { 241 | //============================================= 242 | // option is in form OPT=VAL 243 | //============================================= 244 | agentOption = v.substring(0, index); 245 | ao = decode(agentOption); 246 | optionValue = v.substring(index+1); 247 | if(optionValue.isEmpty()) { 248 | throw new RuntimeException("Empty value for option [" + ao.name() + "]"); 249 | } 250 | } 251 | ao.commandLine(optionValue, allAgentOptions, map); 252 | mandatory.remove(ao); 253 | 254 | } 255 | } 256 | if(!mandatory.isEmpty()) { 257 | throw new RuntimeException("Missing mandatory command line option[s]:" + mandatory); 258 | } 259 | return map; 260 | } 261 | } 262 | -------------------------------------------------------------------------------- /src/main/java/com/heliosapm/jvmti/install/AgentOptionProcessor.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2016 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package com.heliosapm.jvmti.install; 14 | 15 | import java.util.Map; 16 | import java.util.regex.Pattern; 17 | 18 | /** 19 | *

Title: AgentOptionProcessor

20 | *

Description: Defines an operation performed on a command line or agent option

21 | *

Company: Helios Development Group LLC

22 | * @author Whitehead (nwhitehead AT heliosdev DOT org) 23 | *

com.heliosapm.jvmti.install.AgentOptionProcessor

24 | */ 25 | 26 | public interface AgentOptionProcessor { 27 | public static final Pattern NAME_VALUE_SPLITTER = Pattern.compile("="); 28 | public void commandLine(final String value, final StringBuilder agentOpts, final Map extracted); 29 | public void agentOpts(final String value, final Map extracted); 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/com/heliosapm/jvmti/install/JavaAgent.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2016 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package com.heliosapm.jvmti.install; 14 | 15 | import java.io.IOException; 16 | import java.lang.instrument.Instrumentation; 17 | import java.net.URL; 18 | 19 | import org.pmw.tinylog.Configurator; 20 | import org.pmw.tinylog.Logger; 21 | import org.w3c.dom.Node; 22 | 23 | import com.heliosapm.jvmti.agent.NativeAgent; 24 | import com.heliosapm.utils.url.URLHelper; 25 | import com.heliosapm.utils.xml.XMLHelper; 26 | 27 | /** 28 | *

Title: JavaAgent

29 | *

Description: The bootstrap java agent

30 | *

Company: Helios Development Group LLC

31 | * @author Whitehead (nwhitehead AT heliosdev DOT org) 32 | *

com.heliosapm.jvmti.install.JavaAgent

33 | */ 34 | 35 | public class JavaAgent { 36 | /** The installation supplied instrumentation instance */ 37 | public static Instrumentation INSTRUMENTATION = null; 38 | /** The agent arg (location of xml config) */ 39 | private static String xmlConfig = "defaultconfig.xml"; 40 | /** The xml config URL */ 41 | private static URL xmlConfigUrl = null; 42 | /** The xml config XML node */ 43 | private static Node rootConfigNode = null; 44 | 45 | public static void main(String[] args) { 46 | main("", (Instrumentation)null); 47 | } 48 | 49 | /** 50 | * The agent bootstrap entry point 51 | * @param agentArgs The agent initialization arguments 52 | * @param inst The instrumentation instance 53 | */ 54 | public static void main(final String agentArgs, final Instrumentation inst) { 55 | INSTRUMENTATION = inst; 56 | Logger.info("Instrumentation:{}", INSTRUMENTATION!=null); 57 | if(agentArgs!=null && !agentArgs.trim().isEmpty()) { 58 | Logger.info("Supplied Agent Args: [{}]", agentArgs); 59 | URL url = URLHelper.toURL(agentArgs); 60 | if(URLHelper.resolves(url)) { 61 | xmlConfigUrl = url; 62 | } else { 63 | Logger.warn("Supplied XML Config Could Not Be Resolved: [{}]", agentArgs); 64 | } 65 | } 66 | if(xmlConfigUrl==null) { 67 | xmlConfigUrl = URLHelper.toURL(xmlConfig); 68 | } 69 | Logger.info("XML Config: [{}]", xmlConfigUrl); 70 | rootConfigNode = XMLHelper.parseXML(xmlConfigUrl).getDocumentElement(); 71 | Logger.debug("First Child Node: [{}]", XMLHelper.renderNode(rootConfigNode)); 72 | configure(); 73 | } 74 | 75 | private static void configure() { 76 | if(XMLHelper.hasChildNodeByName(rootConfigNode, "logging")) { 77 | externalLoggingConfig(); 78 | } 79 | loadNative(); 80 | } 81 | 82 | private static void externalLoggingConfig() { 83 | Node node = XMLHelper.getChildNodeByName(rootConfigNode, "logging"); 84 | String externalConfig = XMLHelper.getAttributeByName(node, "config", null); 85 | if(URLHelper.resolves(URLHelper.toURL(externalConfig))) { 86 | try { 87 | URL configUrl = URLHelper.toURL(externalConfig); 88 | Configurator.fromURL(configUrl); 89 | Logger.info("Configured logging from external url: [{}]", configUrl); 90 | } catch (IOException iex) { 91 | Logger.warn("Failed to configure logging from external: [{}]", externalConfig, iex); 92 | } 93 | } 94 | } 95 | 96 | private static void loadNative() { 97 | Node node = XMLHelper.getChildNodeByName(rootConfigNode, "native"); 98 | if(node!=null && XMLHelper.getAttributeByName(node, "disabled", false)) { 99 | Logger.info("Native Agent Disabled"); 100 | return; 101 | } 102 | NativeAgent.getInstance(); 103 | } 104 | 105 | /** 106 | * The agent bootstrap entry point 107 | * @param agentArgs The agent initialization arguments 108 | * @param inst The instrumentation instance 109 | */ 110 | public static void agentmain(final String agentArgs, final Instrumentation inst) { 111 | Logger.info("agentmain:2"); 112 | main(agentArgs, inst); 113 | } 114 | 115 | /** 116 | * The agent bootstrap entry point 117 | * @param agentArgs The agent initialization arguments 118 | * @param inst The instrumentation instance 119 | */ 120 | public static void premain(final String agentArgs, final Instrumentation inst) { 121 | Logger.info("premain:2"); 122 | main(agentArgs, inst); 123 | } 124 | 125 | /** 126 | * The agent bootstrap entry point which fails the install since there is no instrumentation 127 | * @param agentArgs The agent initialization arguments 128 | */ 129 | public static void agentmain(final String agentArgs) { 130 | Logger.info("agentmain:1"); 131 | main(agentArgs, null); 132 | } 133 | 134 | /** 135 | * The agent bootstrap entry point which fails the install since there is no instrumentation 136 | * @param agentArgs The agent initialization arguments 137 | */ 138 | public static void premain(final String agentArgs) { 139 | Logger.info("premain:1"); 140 | main(agentArgs, null); 141 | } 142 | 143 | } 144 | -------------------------------------------------------------------------------- /src/main/java/com/heliosapm/jvmti/install/JavaAgent2.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2016 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package com.heliosapm.jvmti.install; 14 | 15 | import java.io.IOException; 16 | import java.lang.instrument.Instrumentation; 17 | import java.lang.management.ManagementFactory; 18 | import java.lang.reflect.Method; 19 | import java.net.URL; 20 | import java.util.Map; 21 | import java.util.Properties; 22 | import java.util.Set; 23 | 24 | import javax.management.ObjectName; 25 | import javax.management.loading.PrivateMLet; 26 | 27 | import org.pmw.tinylog.Configurator; 28 | import org.pmw.tinylog.Logger; 29 | import org.w3c.dom.Node; 30 | 31 | import com.heliosapm.jvmti.extension.ExecutionScheduler; 32 | import com.heliosapm.utils.collections.Props; 33 | import com.heliosapm.utils.concurrency.ExtendedThreadManager; 34 | import com.heliosapm.utils.jmx.JMXHelper; 35 | import com.heliosapm.utils.lang.StringHelper; 36 | import com.heliosapm.utils.url.URLHelper; 37 | import com.heliosapm.utils.xml.XMLHelper; 38 | 39 | /** 40 | *

Title: JavaAgent2

41 | *

Description: The boot java agent implementation to install the agent

42 | *

Company: Helios Development Group LLC

43 | * @author Whitehead (nwhitehead AT heliosdev DOT org) 44 | *

com.heliosapm.jvmti.install.JavaAgent2

45 | */ 46 | 47 | public class JavaAgent2 { 48 | /** The installation supplied instrumentation instance */ 49 | public static Instrumentation INSTRUMENTATION = null; 50 | 51 | /** System property overriding the default MLET MBean */ 52 | public static final String MLET_OBJECT_NAME_PROP = "com.heliosapm.jvmti.classloader.objectname"; 53 | /** The default MLET MBean */ 54 | public static final String MLET_OBJECT_NAME = "com.heliosapm.jvmti.agent:service=ClassLoader"; 55 | /** The class name of the native agent */ 56 | public static final String AGENT_CLASS_NAME = "com.heliosapm.jvmti.agent.Agent"; 57 | 58 | /** The agent arg (location of xml config) */ 59 | private static String xmlConfig = "defaultconfig.xml"; 60 | /** The xml config URL */ 61 | private static URL xmlConfigUrl = null; 62 | /** The xml config XML node */ 63 | private static Node rootConfigNode = null; 64 | 65 | 66 | /** 67 | * The agent bootstrap entry point 68 | * @param agentArgs The agent initialization arguments 69 | * @param inst The instrumentation instance 70 | */ 71 | public static void main(final String agentArgs, final Instrumentation inst) { 72 | System.err.println("DEBUG: Booting NativeAgent...."); 73 | INSTRUMENTATION = inst; 74 | final Map agentOptions = AgentOption.agentOptions(agentArgs); 75 | final Properties p = (Properties)agentOptions.get(AgentOption.D); 76 | if(p!=null && !p.isEmpty()) installProperties(p); 77 | @SuppressWarnings("unchecked") 78 | final Set classPath = (Set)agentOptions.get(AgentOption.CP); 79 | final ClassLoader classLoader = getClassLoader(classPath); 80 | final ClassLoader current = Thread.currentThread().getContextClassLoader(); 81 | try { 82 | Thread.currentThread().setContextClassLoader(classLoader); 83 | System.err.println("DEBUG: Finding class [" + AGENT_CLASS_NAME + "]"); 84 | final Class agentClass = Class.forName(AGENT_CLASS_NAME, true, JavaAgent2.class.getClassLoader()); 85 | System.err.println("DEBUG: Found class [" + agentClass.getName() + "]"); 86 | final Method method = agentClass.getDeclaredMethod("getInstance"); 87 | System.err.println("DEBUG: Found method [" + method.toGenericString() + "]\n\tInvoking....."); 88 | // need to delay the invocation in a seperate thread because 89 | // the Agent will need to attach to itself to load the native agent. 90 | final Thread agentLoader = new Thread("NativeAgentLoader") { 91 | public void run() { 92 | try { 93 | Thread.currentThread().join(500); // TODO: make this configurable 94 | method.invoke(null); 95 | System.err.println("OK: Native Agent Installed. Configuring..."); 96 | xmlConfigUrl = URLHelper.toURL(xmlConfig); 97 | Logger.info("XML Config: [{}]", xmlConfigUrl); 98 | rootConfigNode = XMLHelper.parseXML(xmlConfigUrl).getDocumentElement(); 99 | Logger.debug("First Child Node: [{}]", XMLHelper.renderNode(rootConfigNode)); 100 | configure(); 101 | } catch (Throwable ex) { 102 | System.err.println("Failed to load native agent. Stack trace follows:"); 103 | ex.printStackTrace(System.err); 104 | } 105 | } 106 | }; 107 | agentLoader.setDaemon(true); 108 | agentLoader.start(); 109 | System.err.println("DEBUG: We're done here"); 110 | } catch (Exception ex) { 111 | System.err.println("ERROR: Failed to install Agent:" + ex); 112 | } finally { 113 | Thread.currentThread().setContextClassLoader(current); 114 | } 115 | 116 | } 117 | 118 | protected static void configure() throws Exception { 119 | if(XMLHelper.hasChildNodeByName(rootConfigNode, "logging")) { 120 | sysPropsConfig(); 121 | externalLoggingConfig(); 122 | extensionsConfig(); 123 | jmxmpConfig(); 124 | extendedThreadManagerConfig(); 125 | } 126 | } 127 | 128 | private static void extendedThreadManagerConfig() { 129 | if(XMLHelper.getChildNodeByName(rootConfigNode, "extendedtm") != null) { 130 | ExtendedThreadManager.install(); 131 | } 132 | } 133 | 134 | private static void sysPropsConfig() { 135 | Node node = XMLHelper.getChildNodeByName(rootConfigNode, "sysprops"); 136 | if(node!=null) { 137 | Properties p = Props.strToProps( 138 | StringHelper.resolveTokens( 139 | XMLHelper.getNodeTextValue(node) 140 | ) 141 | ); 142 | Props.setSystem(p); 143 | } 144 | } 145 | 146 | private static void externalLoggingConfig() { 147 | Node node = XMLHelper.getChildNodeByName(rootConfigNode, "logging"); 148 | String externalConfig = XMLHelper.getAttributeByName(node, "config", null); 149 | if(externalConfig != null) { 150 | externalConfig = StringHelper.resolveTokens(externalConfig); 151 | } 152 | if(URLHelper.resolves(URLHelper.toURL(externalConfig))) { 153 | try { 154 | URL configUrl = URLHelper.toURL(externalConfig); 155 | Configurator.fromURL(configUrl); 156 | Logger.info("Configured logging from external url: [{}]", configUrl); 157 | } catch (IOException iex) { 158 | Logger.warn("Failed to configure logging from external: [{}]", externalConfig, iex); 159 | } 160 | } 161 | } 162 | 163 | private static void extensionsConfig() { 164 | Node node = XMLHelper.getChildNodeByName(rootConfigNode, "extensions"); 165 | for(Node xnode : XMLHelper.getChildNodesByName(node, "extension", false)) { 166 | String className = XMLHelper.getNodeTextValue(xnode); 167 | ExecutionScheduler.getInstance().schedule(className); 168 | } 169 | } 170 | 171 | private static void jmxmpConfig() { 172 | Node node = XMLHelper.getChildNodeByName(rootConfigNode, "jmxmp"); 173 | if(node!=null) { 174 | Node portNode = XMLHelper.getChildNodeByName(node, "port"); 175 | if(portNode!=null) { 176 | Node ifaceNode = XMLHelper.getChildNodeByName(node, "iface"); 177 | String iface = ifaceNode==null ? "127.0.0.1" : StringHelper.resolveTokens(XMLHelper.getNodeTextValue(ifaceNode)); 178 | int port = Integer.parseInt(StringHelper.resolveTokens(XMLHelper.getNodeTextValue(portNode))); 179 | JMXHelper.fireUpJMXMPServer(iface, port); 180 | } 181 | } 182 | } 183 | 184 | 185 | protected static ClassLoader getClassLoader(final Set classPath) { 186 | if(classPath==null) return JavaAgent2.class.getClassLoader(); 187 | final PrivateMLet classLoader = new PrivateMLet(classPath.toArray(new URL[classPath.size()]), true); 188 | try { 189 | final ObjectName on = new ObjectName(System.getProperty(MLET_OBJECT_NAME_PROP, MLET_OBJECT_NAME)); 190 | ManagementFactory.getPlatformMBeanServer().registerMBean(classLoader, on); 191 | } catch (Exception ex) { 192 | System.err.println("WARNING: Failed to register PrivateMLet MBean:" + ex); 193 | } 194 | return classLoader; 195 | } 196 | 197 | /** 198 | * Installs agent related system properties 199 | * @param p The properties to install 200 | */ 201 | protected static void installProperties(final Properties p) { 202 | for(final String key: p.stringPropertyNames()) { 203 | System.setProperty(key, p.getProperty(key)); 204 | } 205 | } 206 | 207 | /** 208 | * The agent bootstrap entry point 209 | * @param agentArgs The agent initialization arguments 210 | * @param inst The instrumentation instance 211 | */ 212 | public static void agentmain(final String agentArgs, final Instrumentation inst) { 213 | main(agentArgs, inst); 214 | } 215 | 216 | /** 217 | * The agent bootstrap entry point 218 | * @param agentArgs The agent initialization arguments 219 | * @param inst The instrumentation instance 220 | */ 221 | public static void premain(final String agentArgs, final Instrumentation inst) { 222 | main(agentArgs, inst); 223 | } 224 | 225 | /** 226 | * The agent bootstrap entry point which fails the install since there is no instrumentation 227 | * @param agentArgs The agent initialization arguments 228 | */ 229 | public static void agentmain(final String agentArgs) { 230 | main(agentArgs, null); 231 | } 232 | 233 | /** 234 | * The agent bootstrap entry point which fails the install since there is no instrumentation 235 | * @param agentArgs The agent initialization arguments 236 | */ 237 | public static void premain(final String agentArgs) { 238 | main(agentArgs, null); 239 | } 240 | 241 | } 242 | -------------------------------------------------------------------------------- /src/main/java/com/heliosapm/jvmti/install/NativePlatform.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.heliosapm.jvmti.install; 5 | 6 | import java.io.File; 7 | import java.net.URL; 8 | import java.util.concurrent.atomic.AtomicBoolean; 9 | 10 | import org.pmw.tinylog.Logger; 11 | 12 | import com.heliosapm.utils.url.URLHelper; 13 | 14 | /** 15 | * Supported native platforms 16 | * @author nwhitehead 17 | * 18 | */ 19 | public enum NativePlatform { 20 | LINUX64("LINUX", 64, "linux64", "liboifagent.so", ".so"), 21 | LINUX32("LINUX", 32, "linux32", "liboifagent.so", ".so"), 22 | WINDOWS64("WINDOWS", 64, "win64", "oifagent.dll", ".dll"), 23 | WINDOWS32("WINDOWS", 32, "win32", "oifagent.dll", ".dll"), 24 | NOIMPL(null, -1, null, null, null); 25 | 26 | private NativePlatform(final String os, final int arch, final String libDir, final String libName, final String extension) { 27 | this.os = os; 28 | this.arch = arch; 29 | this.libDir = libDir; 30 | this.libName = libName; 31 | this.extension = extension; 32 | } 33 | 34 | public final String os; 35 | public final int arch; 36 | public final String libDir; 37 | public final String libName; 38 | public final String extension; 39 | 40 | private static final AtomicBoolean nativeLibLoaded = new AtomicBoolean(false); 41 | private static final NativePlatform[] values = values(); 42 | 43 | public static boolean isNativeLibLoaded() { 44 | return nativeLibLoaded.get(); 45 | } 46 | 47 | public static NativePlatform detect() { 48 | NativePlatform current = null; 49 | final String osname = System.getProperty("os.name", "").toUpperCase(); 50 | final int archmodel = Integer.parseInt(System.getProperty("sun.arch.data.model", "-1")); 51 | for(NativePlatform np: values) { 52 | if(osname.contains(np.os) && archmodel==np.arch) current = np; 53 | break; 54 | } 55 | if(current==null) current = NOIMPL; 56 | Logger.info("NativePlatform: {}", current.name()); 57 | if(current==NOIMPL) { 58 | Logger.warn("No native lib implemented for platform [{}/{}]", System.getProperty("os.name"), System.getProperty("sun.arch.data.model")); 59 | } 60 | return current; 61 | } 62 | 63 | public static void load(final String lib) { 64 | if(!nativeLibLoaded.get()) { 65 | if(lib!=null) { 66 | try { 67 | File libFile = new File(lib); 68 | String fullPath = libFile.getAbsolutePath(); 69 | System.load(fullPath); 70 | Logger.info("Loaded native library [{}]", fullPath); 71 | nativeLibLoaded.set(true); 72 | } catch (Throwable t) { 73 | Logger.error("Failed to load native library [{}]", lib, t); 74 | } 75 | } else { 76 | final NativePlatform np = detect(); 77 | URL libUrl = null; 78 | if(NativePlatform.class.getProtectionDomain().getCodeSource().getLocation().toString().endsWith(".jar")) { 79 | try { 80 | libUrl = URLHelper.toURL("native/" + np.libDir + "/" + np.libName); 81 | File f = File.createTempFile("native-agent", np.extension); 82 | f.deleteOnExit(); 83 | URLHelper.writeToFile(libUrl, f, false); 84 | load(f.getAbsolutePath()); 85 | } catch (Exception ex) { 86 | throw new IllegalStateException("Failed t o write extracted native lib", ex); 87 | } 88 | } else { 89 | // We're in DEV mode 90 | load("./target/native/native/" + np.libDir + "/" + np.libName); 91 | } 92 | } 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/com/heliosapm/jvmti/jmx/NativeOps.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.heliosapm.jvmti.jmx; 5 | 6 | import org.pmw.tinylog.Logger; 7 | 8 | import com.heliosapm.jvmti.agent.NativeAgent; 9 | import com.heliosapm.utils.jmx.JMXHelper; 10 | 11 | /** 12 | * @author nwhitehead 13 | * 14 | */ 15 | public class NativeOps implements NativeOpsMXBean { 16 | final NativeAgent nativeAgent; 17 | 18 | public NativeOps() { 19 | nativeAgent = NativeAgent.getInstance(); 20 | JMXHelper.registerMBean(JMXHelper.objectName("com.heliosapm.jvmti.jmx:service=NativeOps"), this); 21 | Logger.info("NativeOps installed"); 22 | } 23 | 24 | @Override 25 | public int getInstanceCountOf(String exactTypeName) { 26 | // TODO Auto-generated method stub 27 | return 0; 28 | } 29 | 30 | @Override 31 | public int getInstanceCountOfAny(String anyTypeName) { 32 | // TODO Auto-generated method stub 33 | return 0; 34 | } 35 | 36 | protected Class getClassForName(String name) { 37 | try { 38 | return null; // TODO 39 | } catch (Exception ex) { 40 | Logger.error("Failed to find class: {}", name, ex); 41 | throw new RuntimeException("Failed to find class: " + name, ex); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/heliosapm/jvmti/jmx/NativeOpsMXBean.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.heliosapm.jvmti.jmx; 5 | 6 | /** 7 | * @author nwhitehead 8 | * 9 | */ 10 | public interface NativeOpsMXBean { 11 | /** 12 | * Counts the number of heap objects of the exact passed type 13 | * @param exactTypeName The name of the exact type of heap objects to count 14 | * @return the number of objects found on the heap 15 | */ 16 | public int getInstanceCountOf(final String exactTypeName); 17 | 18 | /** 19 | * Counts the number of heap objects of the passed type or any type inherrited from it 20 | * @param anyTypeName The type name of heap objects to count 21 | * @return the number of objects found on the heap 22 | */ 23 | public int getInstanceCountOfAny(final String anyTypeName); 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/heliosapm/jvmti/metrics/LongGauge.java: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | */ 4 | package com.heliosapm.jvmti.metrics; 5 | 6 | import java.util.concurrent.atomic.AtomicLong; 7 | 8 | import com.codahale.metrics.Gauge; 9 | 10 | /** 11 | * @author nwhitehead 12 | * 13 | */ 14 | public class LongGauge implements Gauge { 15 | final AtomicLong value = new AtomicLong(); 16 | 17 | @Override 18 | public Long getValue() { 19 | return value.get(); 20 | } 21 | 22 | public void update(long value) { 23 | this.value.set(value); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/heliosapm/jvmti/script/ScriptManager.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2016 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package com.heliosapm.jvmti.script; 14 | 15 | import java.io.File; 16 | import java.io.FileNotFoundException; 17 | import java.io.FileReader; 18 | import java.util.concurrent.ConcurrentHashMap; 19 | import java.util.regex.Pattern; 20 | 21 | import javax.script.Compilable; 22 | import javax.script.CompiledScript; 23 | import javax.script.ScriptEngine; 24 | import javax.script.ScriptEngineManager; 25 | import javax.script.ScriptException; 26 | 27 | /** 28 | *

Title: ScriptManager

29 | *

Description:

30 | *

Company: Helios Development Group LLC

31 | * @author Whitehead (nwhitehead AT heliosdev DOT org) 32 | *

com.heliosapm.jvmti.script.ScriptManager

33 | */ 34 | 35 | public class ScriptManager { 36 | /** The singleton instance */ 37 | private static volatile ScriptManager instance = null; 38 | /** The singleton instance ctor lock */ 39 | private static Object lock = new Object(); 40 | 41 | /** The script engine manager */ 42 | private final ScriptEngineManager sem; 43 | /** A map of script engines keyed by the lower case extension */ 44 | private final ConcurrentHashMap scriptEngines = new ConcurrentHashMap(16); 45 | 46 | 47 | class FileTs { 48 | final File scriptFile; 49 | long timestamp = -1; 50 | CompiledScript cs = null; 51 | final ScriptEngine se; 52 | 53 | FileTs(final String fileName) { 54 | scriptFile = new File(fileName); 55 | timestamp = scriptFile.lastModified(); 56 | final int index = fileName.lastIndexOf('.'); 57 | final String ext; 58 | if(index==-1) { 59 | ext = "js"; 60 | } else { 61 | ext = fileName.substring(index+1); 62 | } 63 | se = scriptEngines.get(ext); 64 | if(se==null) throw new RuntimeException("No script engine for file [" + fileName + "]"); 65 | compile(); 66 | } 67 | 68 | void compile() { 69 | FileReader fr = null; 70 | try { 71 | fr = new FileReader(scriptFile); 72 | ((Compilable)se).compile(fr); 73 | } catch (FileNotFoundException e) { 74 | throw new RuntimeException(e); 75 | } catch (ScriptException e) { 76 | throw new RuntimeException(e); 77 | } finally { 78 | try { fr.close(); } catch (Exception x) {/* No Op */} 79 | } 80 | } 81 | } 82 | 83 | /** 84 | * Acquires the singleton ScriptManager instance 85 | * @return the ScriptManager 86 | */ 87 | public static ScriptManager getInstance() { 88 | if(instance==null) { 89 | synchronized(lock) { 90 | if(instance==null) { 91 | instance = new ScriptManager(); 92 | } 93 | } 94 | } 95 | return instance; 96 | } 97 | 98 | private ScriptManager() { 99 | sem = new ScriptEngineManager(getClass().getClassLoader()); 100 | // sem.getEngineFactories().stream() 101 | // .forEach(sef -> { 102 | // final ScriptEngine se = sef.getScriptEngine(); 103 | // if(se instanceof Compilable) { 104 | // sef.getExtensions().stream().forEach(ext -> { 105 | // scriptEngines.put(ext.toLowerCase(), se); 106 | // }); 107 | // } 108 | // }); 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /src/main/java/com/heliosapm/jvmti/util/IsolatedClassLoaderMBean.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2016 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package com.heliosapm.jvmti.util; 14 | 15 | import java.net.URL; 16 | 17 | /** 18 | *

Title: IsolatedClassLoaderMBean

19 | *

Description: JMX MBean interface for {@link IsolatedClassLoader} instances

20 | *

Company: Helios Development Group LLC

21 | * @author Whitehead (nwhitehead AT heliosdev DOT org) 22 | *

com.heliosapm.jvmti.util.IsolatedClassLoaderMBean

23 | */ 24 | 25 | public interface IsolatedClassLoaderMBean { 26 | /** 27 | * Returns the URLs that comprise the classloaders isolated classpath 28 | * @return an array of URLs 29 | */ 30 | public URL[] getURLs(); 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/heliosapm/jvmti/util/SystemClock.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2016 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package com.heliosapm.jvmti.util; 14 | 15 | import java.lang.management.ManagementFactory; 16 | import java.util.Collections; 17 | import java.util.EnumMap; 18 | import java.util.Map; 19 | import java.util.concurrent.TimeUnit; 20 | 21 | 22 | /** 23 | *

Title: SystemClock

24 | *

Description:

25 | *

Company: Helios Development Group LLC

26 | * @author Whitehead (nwhitehead AT heliosdev DOT org) 27 | *

com.heliosapm.jvmti.util.SystemClock

28 | */ 29 | 30 | public class SystemClock { 31 | 32 | /** 33 | * Starts a new timer 34 | * @return the elapsed time object on which elapsed times can be drawn 35 | */ 36 | public static ElapsedTime startClock() { 37 | return new ElapsedTime(); 38 | } 39 | 40 | 41 | /** 42 | * Sleeps for the specified number of ms The number of ms to sleep for. 43 | * @param ms 44 | */ 45 | public static void sleep(final long ms) { 46 | try { Thread.currentThread().join(ms); } catch (Exception ex) { 47 | throw new RuntimeException(ex); 48 | } 49 | } 50 | 51 | /** 52 | * Sleeps for the specified period 53 | * @param time The time to sleep for 54 | * @param unit The unit of time. If null, defaults to {@link TimeUnit#MILLISECONDS} 55 | */ 56 | public static void sleep(final long time, final TimeUnit unit) { 57 | final TimeUnit u = unit==null ? TimeUnit.MILLISECONDS : unit; 58 | sleep(TimeUnit.MILLISECONDS.convert(time, u)); 59 | } 60 | 61 | 62 | /** 63 | * Returns the current time in ms. 64 | * @return the current time in ms. 65 | */ 66 | public static long time() { 67 | return System.currentTimeMillis(); 68 | } 69 | 70 | /** 71 | * Returns the current time in milliseconds to second precision 72 | * @return the second precision current timestamp in ms. 73 | */ 74 | public static long rtime() { 75 | return TimeUnit.MILLISECONDS.convert(TimeUnit.SECONDS.convert(time(), TimeUnit.MILLISECONDS), TimeUnit.SECONDS); 76 | } 77 | 78 | /** 79 | * Returns a JDBC timestamp for the current time 80 | * @return a JDBC timestamp for the current time 81 | */ 82 | public static java.sql.Timestamp getTimestamp() { 83 | return new java.sql.Timestamp(time()); 84 | } 85 | 86 | /** 87 | * Returns the current time in Unix Time (s.) 88 | * @return the current time in Unix Time 89 | */ 90 | public static long unixTime() { 91 | return TimeUnit.SECONDS.convert(System.currentTimeMillis(), TimeUnit.MILLISECONDS); 92 | } 93 | 94 | /** 95 | * Returns the JVM up time in ms. 96 | * @return the up time in ms. 97 | */ 98 | public static long upTime() { 99 | return ManagementFactory.getRuntimeMXBean().getUptime(); 100 | } 101 | 102 | /** 103 | * Returns the JVM up time in SNMP tick time. 104 | * @return the up time in SNMP tick time 105 | */ 106 | public static long upTimeTicks() { 107 | return toTicks(ManagementFactory.getRuntimeMXBean().getUptime()); 108 | } 109 | 110 | 111 | /** 112 | * Converts the passed time to SNMP ticks 113 | * @param time The time to convert 114 | * @param unit The unit the time is in 115 | * @return the time in SNMP ticks 116 | */ 117 | public static long toTicks(final long time, final TimeUnit unit) { 118 | long tt = TimeUnit.MILLISECONDS.convert(time, unit)/100; 119 | return tt; 120 | } 121 | 122 | /** 123 | * Converts the passed time to SNMP ticks 124 | * @param time The time to convert 125 | * @return the time in SNMP ticks 126 | */ 127 | public static long toTicks(final long time) { 128 | return toTicks(time, TimeUnit.MILLISECONDS); 129 | } 130 | 131 | 132 | /** 133 | * Returns the relative time in ns. 134 | * @return the relative time in ms. 135 | */ 136 | public static long timens() { 137 | return System.nanoTime(); 138 | } 139 | 140 | /** 141 | * Returns the current time in SNMP {@link TimeTicks} equivalent or 1/100th seconds 142 | * @return the current time in SNMP {@link TimeTicks} 143 | */ 144 | public static long tick() { 145 | long tt = time()/100; 146 | return tt; 147 | } 148 | 149 | 150 | 151 | 152 | /** 153 | *

Title: ElapsedTime

154 | *

Description: An elapsed time reporter

155 | *

Company: Helios Development Group LLC

156 | * @author Whitehead (nwhitehead AT heliosdev DOT org) 157 | *

com.heliosapm.utils.time.SystemClock.ElapsedTime

158 | * TODO: Lots.... 159 | * Format multiple time units, avg units 160 | * Lap times 161 | */ 162 | public static class ElapsedTime { 163 | /** The start time in ns. */ 164 | public final long startNs; 165 | /** The last lap end time in ns. */ 166 | public long endNs; 167 | 168 | /** 169 | * Creates a new ElapsedTime 170 | */ 171 | private ElapsedTime(){ 172 | startNs = System.nanoTime(); 173 | } 174 | 175 | /** 176 | * Returns the start time in ns. 177 | * @return the start time in ns. 178 | */ 179 | public long startTime() { 180 | return startNs; 181 | } 182 | 183 | /** 184 | * Returns the start time in ms. 185 | * @return the start time in ms. 186 | */ 187 | public long startTimeMs() { 188 | return TimeUnit.MILLISECONDS.convert(startNs, TimeUnit.NANOSECONDS); 189 | } 190 | 191 | /** 192 | * Returns the start time in s. 193 | * @return the start time in s. 194 | */ 195 | public long startTimeS() { 196 | return TimeUnit.SECONDS.convert(startNs, TimeUnit.NANOSECONDS); 197 | } 198 | 199 | 200 | 201 | 202 | /** Some extended time unit entries */ 203 | public static final Map UNITS; 204 | 205 | static { 206 | Map tmp = new EnumMap(TimeUnit.class); 207 | tmp.put(TimeUnit.DAYS, "days"); 208 | tmp.put(TimeUnit.HOURS, "hrs."); 209 | tmp.put(TimeUnit.MICROSECONDS, "\u00b5s."); 210 | tmp.put(TimeUnit.MILLISECONDS, "ms."); 211 | tmp.put(TimeUnit.MINUTES, "min."); 212 | tmp.put(TimeUnit.NANOSECONDS, "ns."); 213 | tmp.put(TimeUnit.SECONDS, "s."); 214 | UNITS = Collections.unmodifiableMap(tmp); 215 | } 216 | 217 | 218 | 219 | // private ElapsedTime(boolean lap, long endTime) { 220 | // endNs = endTime; 221 | // startNs = timerStart.get()[0]; 222 | // long[] lastLapRead = lapTime.get(); 223 | // if(lastLapRead!=null) { 224 | // lastLapNs = lastLapRead[0]; 225 | // } 226 | // if(lap) { 227 | // lapTime.set(new long[]{endTime}); 228 | // } else { 229 | // timerStart.remove(); 230 | // lapTime.remove(); 231 | // } 232 | // elapsedNs = endNs-startNs; 233 | // elapsedMs = TimeUnit.MILLISECONDS.convert(elapsedNs, TimeUnit.NANOSECONDS); 234 | // if(lastLapNs!=-1L) { 235 | // elapsedSinceLastLapNs = endTime -lastLapNs; 236 | // elapsedSinceLastLapMs = TimeUnit.MILLISECONDS.convert(elapsedSinceLastLapNs, TimeUnit.NANOSECONDS); 237 | // } 238 | // 239 | // } 240 | /** 241 | * Returns the average elapsed time in ms. for the passed number of events 242 | * @param cnt The number of events 243 | * @return The average elapsed time in ms. 244 | */ 245 | public long avgMs(double cnt) { 246 | return _avg(elapsed(TimeUnit.MILLISECONDS), cnt); 247 | } 248 | 249 | /** 250 | * Returns the average elapsed time in ns. for the passed number of events 251 | * @param cnt The number of events 252 | * @return The average elapsed time in ns. 253 | */ 254 | public long avgNs(double cnt) { 255 | long elapsedNs = System.nanoTime()-startNs; 256 | return _avg(elapsedNs, cnt); 257 | } 258 | 259 | 260 | private static long _avg(double time, double cnt) { 261 | if(time==0 || cnt==0 ) return 0L; 262 | double d = time/cnt; 263 | return Math.round(d); 264 | } 265 | 266 | /** 267 | * Returns the elapsed time since start in ns. 268 | * @return elapsed ns. 269 | */ 270 | public long elapsed() { 271 | return elapsed(TimeUnit.NANOSECONDS); 272 | } 273 | 274 | /** 275 | * Returns the elapsed time since start in ms. 276 | * @return elapsed ms. 277 | */ 278 | public long elapsedMs() { 279 | return elapsed(TimeUnit.MILLISECONDS); 280 | } 281 | 282 | /** 283 | * Returns the elapsed time since start in s. 284 | * @return elapsed s. 285 | */ 286 | public long elapsedS() { 287 | return elapsed(TimeUnit.SECONDS); 288 | } 289 | 290 | /** 291 | * Returns the elapsed time since start in the passed unit 292 | * @param unit The unit to report elapsed time in 293 | * @return the elapsed time 294 | */ 295 | public long elapsed(TimeUnit unit) { 296 | long elapsedNs = System.nanoTime()-startNs; 297 | if(unit==null) unit = TimeUnit.NANOSECONDS; 298 | return unit.convert(elapsedNs, TimeUnit.NANOSECONDS); 299 | } 300 | 301 | /** 302 | * Returns the decorated elapsed time since start in the passed unit 303 | * @param unit The unit to report elapsed time in 304 | * @return the decorated elapsed time 305 | */ 306 | public String elapsedStr(TimeUnit unit) { 307 | long elapsedNs = System.nanoTime()-startNs; 308 | if(unit==null) unit = TimeUnit.NANOSECONDS; 309 | return new StringBuilder("[").append(unit.convert(elapsedNs, TimeUnit.NANOSECONDS)).append("] ").append(UNITS.get(unit)).toString(); 310 | } 311 | 312 | /** 313 | * Returns the decorated elapsed time since start in ns. 314 | * @return the decorated elapsed time since start in ns. 315 | */ 316 | public String elapsedStr() { 317 | return elapsedStr(TimeUnit.NANOSECONDS); 318 | } 319 | 320 | /** 321 | * Returns the decorated elapsed time since start in ms. 322 | * @return the decorated elapsed time since start in ms. 323 | */ 324 | public String elapsedStrMs() { 325 | return elapsedStr(TimeUnit.MILLISECONDS); 326 | } 327 | 328 | public String printAvg(String unitName, double cnt) { 329 | endNs = System.nanoTime(); 330 | long elapsedNs = endNs - startNs; 331 | long avgNs = _avg(elapsedNs, cnt); 332 | return String.format("Completed %s %s in %s ms. AvgPer: %s ms/%s \u00b5s/%s ns.", 333 | (long)cnt, 334 | unitName, 335 | TimeUnit.MILLISECONDS.convert(elapsedNs, TimeUnit.NANOSECONDS), 336 | TimeUnit.MILLISECONDS.convert(avgNs, TimeUnit.NANOSECONDS), 337 | TimeUnit.MICROSECONDS.convert(avgNs, TimeUnit.NANOSECONDS), 338 | avgNs 339 | ); 340 | } 341 | 342 | public static String printAvg(String unitName, double cnt, final long elapsed, final TimeUnit unit) { 343 | final long elapsedNs = TimeUnit.NANOSECONDS.convert(elapsed, unit); 344 | long avgNs = _avg(elapsedNs, cnt); 345 | return String.format("Completed %s %s in %s ms. AvgPer: %s ms/%s \u00b5s/%s ns.", 346 | (long)cnt, 347 | unitName, 348 | TimeUnit.MILLISECONDS.convert(elapsedNs, TimeUnit.NANOSECONDS), 349 | TimeUnit.MILLISECONDS.convert(avgNs, TimeUnit.NANOSECONDS), 350 | TimeUnit.MICROSECONDS.convert(avgNs, TimeUnit.NANOSECONDS), 351 | avgNs 352 | ); 353 | } 354 | 355 | 356 | } 357 | 358 | 359 | } 360 | -------------------------------------------------------------------------------- /src/main/java/com/heliosapm/jvmti/util/TimerHistory.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2016 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package com.heliosapm.jvmti.util; 14 | 15 | import java.nio.ByteBuffer; 16 | import java.nio.LongBuffer; 17 | import java.util.Arrays; 18 | import java.util.LongSummaryStatistics; 19 | import java.util.concurrent.ThreadLocalRandom; 20 | import java.util.concurrent.atomic.AtomicInteger; 21 | import java.util.concurrent.atomic.AtomicLong; 22 | import java.util.stream.LongStream; 23 | 24 | import com.heliosapm.jvmti.util.SystemClock.ElapsedTime; 25 | 26 | /** 27 | *

Title: TimerHistory

28 | *

Description: A fixed size long time manager

29 | *

Company: Helios Development Group LLC

30 | * @author Whitehead (nwhitehead AT heliosdev DOT org) 31 | *

com.heliosapm.jvmti.util.TimerHistory

32 | */ 33 | 34 | public class TimerHistory { 35 | /** The long array holder */ 36 | private final LongBuffer arr; 37 | /** The max size of the array */ 38 | private final int maxSize; 39 | /** The compact position */ 40 | private final int compactPos; 41 | /** The total number of added points */ 42 | private final AtomicLong count = new AtomicLong(0L); 43 | 44 | /** The current size */ 45 | private final AtomicInteger size = new AtomicInteger(0); 46 | /** The concurrency guard */ 47 | private final AtomicLong guard = new AtomicLong(-1L); 48 | 49 | /** 50 | * Creates a new TimerHistory 51 | * @param size The size of the array 52 | */ 53 | public TimerHistory(final int size) { 54 | if(size < 1) throw new IllegalArgumentException("Invalid size:" + size); 55 | maxSize = size; 56 | compactPos = maxSize-1; 57 | arr = ByteBuffer.allocateDirect(size * 8).asLongBuffer(); 58 | } 59 | 60 | private static final long[] EMPTY_ARR = {}; 61 | private static final LongStream EMPTY_STREAM = LongStream.empty(); 62 | 63 | private long[] asArray() { 64 | final int s = size.get(); 65 | if(s==0) return EMPTY_ARR; 66 | final long[] larr = new long[s]; 67 | arr.position(0); 68 | arr.slice().get(larr, 0, Math.min(s, maxSize)); 69 | return larr; 70 | } 71 | 72 | private void lock(final boolean barge) { 73 | final long id = Thread.currentThread().getId(); 74 | while(true) { 75 | if(guard.compareAndSet(-1L, id)) break; 76 | if(!barge) Thread.yield(); 77 | } 78 | } 79 | 80 | private void unlock() { 81 | final long id = Thread.currentThread().getId(); 82 | if(guard.get()!=id) throw new IllegalStateException("Not locked by calling thread"); 83 | guard.set(-1L); 84 | } 85 | 86 | /* 87 | * [][][] 88 | * [a][][] 89 | * [a][b][] 90 | * [a][b][c] 91 | * [b][c][d] 92 | * [c][d][e] 93 | */ 94 | 95 | public static void log(Object fmt, Object...args) { 96 | System.out.println(String.format(fmt.toString(), args)); 97 | } 98 | 99 | 100 | public void add(final long value) { 101 | try { 102 | lock(false); 103 | final int s = size.get(); 104 | if(s < maxSize) { 105 | arr.put(s, value); 106 | size.incrementAndGet(); 107 | } else { 108 | arr.position(1); 109 | arr.compact(); 110 | arr.put(compactPos, value); 111 | } 112 | count.incrementAndGet(); 113 | } finally { 114 | unlock(); 115 | } 116 | } 117 | 118 | public LongStream stream() { 119 | try { 120 | lock(false); 121 | if(size.get()==0) return EMPTY_STREAM; 122 | return LongStream.of(asArray()).parallel(); 123 | } finally { 124 | unlock(); 125 | } 126 | } 127 | 128 | public long count() { 129 | return count.get(); 130 | } 131 | 132 | public double average() { 133 | return stream().average().orElse(-1L); 134 | } 135 | 136 | public long max() { 137 | return stream().max().orElse(-1L); 138 | } 139 | 140 | public long min() { 141 | return stream().min().orElse(-1L); 142 | } 143 | 144 | public LongSummaryStatistics stats() { 145 | return stream().summaryStatistics(); 146 | } 147 | 148 | 149 | public long last() { 150 | try { 151 | lock(false); 152 | if(size.get()==0) return -1L; 153 | return arr.get(0); 154 | } finally { 155 | unlock(); 156 | } 157 | } 158 | 159 | /** 160 | * Resets the array but not the count 161 | */ 162 | public void reset() { 163 | try { 164 | size.set(0); 165 | lock(true); 166 | } finally { 167 | unlock(); 168 | } 169 | } 170 | 171 | /** 172 | * Resets the array and the count 173 | */ 174 | public void resetAll() { 175 | try { 176 | size.set(0); 177 | count.set(0L); 178 | lock(true); 179 | } finally { 180 | unlock(); 181 | } 182 | } 183 | 184 | 185 | 186 | public static void main(String[] args) { 187 | final ThreadLocalRandom r = ThreadLocalRandom.current(); 188 | final long[] samples = new long[1000]; 189 | for(int i = 0; i < 1000; i++) { 190 | samples[i] = Math.abs(r.nextLong(1000)); 191 | } 192 | final TimerHistory t = new TimerHistory(1000); 193 | for(int i = 0; i < 100000; i++) { 194 | t.add(Math.abs(r.nextLong(1000))); 195 | } 196 | t.reset(); 197 | for(int i = 0; i < 1000; i++) { 198 | t.add(samples[i]); 199 | } 200 | final ElapsedTime et = SystemClock.startClock(); 201 | for(int x = 0; x < 10; x++) { 202 | for(int i = 0; i < 1000; i++) { 203 | t.add(samples[i]); 204 | } 205 | } 206 | log(et.printAvg("Samples", 10000)); 207 | log("Average : %s", (long)t.stream().average().getAsDouble()); 208 | log("Distinct : %s", t.stream().distinct().toArray().length); 209 | log("First : %s", t.stream().findFirst().getAsLong()); 210 | log("Max : %s", t.stream().max().getAsLong()); 211 | log("Min : %s", t.stream().min().getAsLong()); 212 | log("Sum : %s", t.stream().sum()); 213 | log("Count : %s", t.count()); 214 | log("Summary : %s", t.stream().summaryStatistics()); 215 | } 216 | 217 | 218 | public String toString() { 219 | final long[] larr; 220 | try { 221 | lock(true); 222 | larr = asArray(); 223 | } finally { 224 | unlock(); 225 | } 226 | return arr.toString() + ":" + Arrays.toString(larr); 227 | } 228 | 229 | 230 | } 231 | -------------------------------------------------------------------------------- /src/main/java/com/heliosapm/shorthand/attach/vm/AttachProvider.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Helios, OpenSource Monitoring 3 | * Brought to you by the Helios Development Group 4 | * 5 | * Copyright 2007, Helios Development Group and individual contributors 6 | * as indicated by the @author tags. See the copyright.txt file in the 7 | * distribution for a full listing of individual contributors. 8 | * 9 | * This is free software; you can redistribute it and/or modify it 10 | * under the terms of the GNU Lesser General Public License as 11 | * published by the Free Software Foundation; either version 2.1 of 12 | * the License, or (at your option) any later version. 13 | * 14 | * This software is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | * Lesser General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Lesser General Public 20 | * License along with this software; if not, write to the Free 21 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 22 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 23 | * 24 | */ 25 | package com.heliosapm.shorthand.attach.vm; 26 | 27 | import java.lang.reflect.Method; 28 | import java.util.ArrayList; 29 | import java.util.Collection; 30 | import java.util.Collections; 31 | import java.util.List; 32 | import java.util.Map; 33 | import java.util.concurrent.ConcurrentHashMap; 34 | import java.util.logging.Logger; 35 | 36 | 37 | 38 | /** 39 | *

Title: AttachProvider

40 | *

Description: Wrapper class for Attach API's {@link com.sun.tools.attach.spi.AttachProvider}

41 | *

Company: Helios Development Group LLC

42 | * @author Whitehead (nwhitehead AT heliosdev DOT org) 43 | *

com.heliosapm.shorthand.attach.vm.AttachProvider

44 | */ 45 | public class AttachProvider extends BaseWrappedClass { 46 | /** A map of attach provider delegates keyed by their system identity hash codes */ 47 | private static final Map apInstances = new ConcurrentHashMap(); 48 | /** Static class logger */ 49 | private final static Logger log = Logger.getLogger(AttachProvider.class.getName()); 50 | 51 | /** 52 | * Returns a collection of all known attach providers 53 | * @return a collection of attach providers 54 | */ 55 | public static Collection getAttachProviders() { 56 | return Collections.unmodifiableCollection(apInstances.values()); 57 | } 58 | 59 | /** 60 | * Acquires the wrapped AttachProvider for the passed delegate 61 | * @param delegate The AttachProvider delegate object 62 | * @return a wrapped AttachProvider 63 | */ 64 | public static AttachProvider getInstance(Object delegate) { 65 | if(delegate==null) throw new IllegalArgumentException("The passed AttachProvider delegate was null", new Throwable()); 66 | if(!VirtualMachineBootstrap.getInstance().isInstanceOf(delegate, VirtualMachineBootstrap.ATTACH_PROVIDER_CLASS)) { 67 | throw new IllegalArgumentException("The passed delegate of type [" + delegate.getClass().getName() + "] was not of the type [" + VirtualMachineBootstrap.ATTACH_PROVIDER_CLASS + "]", new Throwable()); 68 | } 69 | int id = System.identityHashCode(delegate); 70 | AttachProvider ap = apInstances.get(id); 71 | if(ap==null) { 72 | synchronized(apInstances) { 73 | ap = apInstances.get(id); 74 | if(ap==null) { 75 | ap = new AttachProvider(delegate); 76 | apInstances.put(id, ap); 77 | } 78 | } 79 | } 80 | return ap; 81 | } 82 | 83 | /** 84 | * Lists the Java virtual machines known to this provider. 85 | * @return The list of virtual machine descriptors which describe the Java virtual machines known to this provider (may be empty). 86 | */ 87 | public List listVirtualMachines() { 88 | List results = new ArrayList(); 89 | try { 90 | pushCl(); 91 | List vmds = (List)invoke(delegate, null, "listVirtualMachines"); 92 | for(Object vmd: vmds) { 93 | results.add(VirtualMachineDescriptor.getInstance(vmd)); 94 | } 95 | } finally { 96 | popCl(); 97 | } 98 | return results; 99 | } 100 | 101 | /** 102 | * Attaches to a Java virtual machine. 103 | * @param id The abstract identifier that identifies the Java virtual machine. 104 | * @return VirtualMachine representing the target virtual machine. 105 | */ 106 | public VirtualMachine attachVirtualMachine(String id) { 107 | try { 108 | pushCl(); 109 | return VirtualMachine.getInstance(invoke(delegate, null, "attachVirtualMachineS", id)); 110 | } finally { 111 | popCl(); 112 | } 113 | } 114 | 115 | /** 116 | * Attaches to a Java virtual machine. 117 | * @param vmd The virtual machine descriptor 118 | * @return VirtualMachine representing the target virtual machine. 119 | */ 120 | public VirtualMachine attachVirtualMachine(VirtualMachineDescriptor vmd) { 121 | try { 122 | pushCl(); 123 | return VirtualMachine.getInstance(invoke(delegate, null, "attachVirtualMachineV", vmd.delegate)); 124 | } finally { 125 | popCl(); 126 | } 127 | } 128 | 129 | /** 130 | * Return this provider's name. 131 | * @return This provider's name 132 | */ 133 | public String name() { 134 | try { 135 | pushCl(); 136 | return (String)invoke(delegate, null, "name"); 137 | } finally { 138 | popCl(); 139 | } 140 | } 141 | 142 | /** 143 | * Return this provider's type. 144 | * @return this provider's type. 145 | */ 146 | public String type() { 147 | try { 148 | pushCl(); 149 | return (String)invoke(delegate, null, "type"); 150 | } finally { 151 | popCl(); 152 | } 153 | } 154 | 155 | /** 156 | * Returns a list of the installed attach providers. 157 | * @return A list of the installed attach providers. 158 | */ 159 | public static List providers() { 160 | return Collections.unmodifiableList(new ArrayList(getAttachProviders())); 161 | } 162 | 163 | /** 164 | * Initializes the list of know attach providers 165 | */ 166 | static void init() { 167 | VirtualMachineBootstrap.findAttachAPI(); 168 | try { 169 | pushCl(); 170 | // log.info("Loading Attach Provider with [" + Thread.currentThread().getContextClassLoader() + "]"); 171 | Class clazz = Thread.currentThread().getContextClassLoader().loadClass(VirtualMachineBootstrap.ATTACH_PROVIDER_CLASS); 172 | Method m = clazz.getDeclaredMethod("providers"); 173 | m.setAccessible(true); 174 | List aps = (List)m.invoke(null); 175 | for(Object del: aps) { 176 | getInstance(del); 177 | } 178 | } catch (Exception e) { 179 | throw new RuntimeException("Failed to initialize AttachProvider Cache", e); 180 | } finally { 181 | popCl(); 182 | } 183 | } 184 | 185 | /** 186 | * Creates a new AttachProvider 187 | * @param delegate the delegate object 188 | */ 189 | private AttachProvider(Object delegate) { 190 | super(delegate); 191 | } 192 | 193 | } 194 | -------------------------------------------------------------------------------- /src/main/java/com/heliosapm/shorthand/attach/vm/BaseWrappedClass.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Helios, OpenSource Monitoring 3 | * Brought to you by the Helios Development Group 4 | * 5 | * Copyright 2007, Helios Development Group and individual contributors 6 | * as indicated by the @author tags. See the copyright.txt file in the 7 | * distribution for a full listing of individual contributors. 8 | * 9 | * This is free software; you can redistribute it and/or modify it 10 | * under the terms of the GNU Lesser General Public License as 11 | * published by the Free Software Foundation; either version 2.1 of 12 | * the License, or (at your option) any later version. 13 | * 14 | * This software is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | * Lesser General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Lesser General Public 20 | * License along with this software; if not, write to the Free 21 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 22 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 23 | * 24 | */ 25 | package com.heliosapm.shorthand.attach.vm; 26 | 27 | import java.lang.reflect.Method; 28 | import java.util.HashMap; 29 | import java.util.Map; 30 | import java.util.concurrent.ConcurrentHashMap; 31 | import java.util.logging.Level; 32 | import java.util.logging.Logger; 33 | 34 | 35 | 36 | /** 37 | *

Title: BaseWrappedClass

38 | *

Description: Base class for reflected access class wrappers

39 | *

Company: Helios Development Group LLC

40 | * @author Whitehead (nwhitehead AT heliosdev DOT org) 41 | *

com.heliosapm.shorthand.attach.vm.BaseWrappedClass

42 | */ 43 | public abstract class BaseWrappedClass { 44 | /** The attach API delegate VirtualMachine */ 45 | protected final Object delegate; 46 | /** Method mapping synchronization lock */ 47 | protected final Object synchLock = new Object(); 48 | /** The method mapping for this class */ 49 | protected Map methods = null; 50 | 51 | /** The reflected class methods keyed by the standard method encoding name */ 52 | protected static final Map, Map> methodMap = new ConcurrentHashMap, Map>(); 53 | 54 | /** Thread local to save (and restore) a calling thread's context classloader */ 55 | protected static final ThreadLocal savedState = new ThreadLocal(); 56 | 57 | /** Static class logger */ 58 | private final Logger log = Logger.getLogger(getClass().getName()); 59 | 60 | 61 | /** 62 | * Saves the calling thread's context class loader and replaces it with the VM class loader if it's thread local saved state is null 63 | */ 64 | public static void pushCl() { 65 | if(savedState.get()==null) { 66 | savedState.set(Thread.currentThread().getContextClassLoader()); 67 | ClassLoader cl = VirtualMachineBootstrap.attachClassLoader.get(); 68 | Thread.currentThread().setContextClassLoader(cl==null ? ClassLoader.getSystemClassLoader() : cl); 69 | //log("Pushed ClassLoader [" + Thread.currentThread().getContextClassLoader() + "]"); 70 | } 71 | } 72 | 73 | /** 74 | * Restored the calling thread's context class loader if it's thread local saved state is not null. 75 | */ 76 | public static void popCl() { 77 | if(savedState.get()!=null) { 78 | Thread.currentThread().setContextClassLoader(savedState.get()); 79 | savedState.remove(); 80 | } 81 | } 82 | 83 | /** 84 | * Reflective invocation 85 | * @param delegate The target object to invoke against. Ignored if method is static. 86 | * @param delegateType The class of the delegate . Ignored if the actual delegate is passed. 87 | * @param methodEncode The method encode key 88 | * @param args The arguments to pass to the method invocation 89 | * @return The return value of the method invocation 90 | */ 91 | protected static Object invoke(Object delegate, String delegateType, String methodEncode, Object...args) { 92 | Method m = null; 93 | try { 94 | if(delegate==null && delegateType==null) throw new IllegalArgumentException("The passed delegate and delegate type was null. One must be provided", new Throwable()); 95 | if(methodEncode==null) throw new IllegalArgumentException("The passed methodEncode was null", new Throwable()); 96 | Class delegateClass = null; 97 | 98 | if(delegate!=null) { 99 | delegateClass = delegate.getClass(); 100 | } else { 101 | delegateClass = VirtualMachineBootstrap.getInstance().classCache.get(delegateType); 102 | } 103 | if(delegateClass==null) throw new IllegalArgumentException("Could not determine delegate class", new Throwable()); 104 | Map mMap = getMethodMap(delegateClass); 105 | m = mMap.get(methodEncode); 106 | if(m==null) throw new IllegalArgumentException("The passed methodEncode [" + methodEncode + "] does not map to a delegate method", new Throwable()); 107 | return m.invoke(java.lang.reflect.Modifier.isStatic(m.getModifiers()) ? null : delegate, args); 108 | } catch (Exception e) { 109 | throw new VirtualMachineInvocationException("Failed to invoke [" + (m==null ? methodEncode : m.toGenericString()) + "]", e); 110 | } 111 | } 112 | 113 | /** 114 | * Retrieves the method map for the passed class, climbing the type hierarchy if necessary 115 | * @param clazz The class to get the method map for 116 | * @return The method map for the passed class 117 | */ 118 | protected static Map getMethodMap(Class clazz) { 119 | Map mMap = null; 120 | Class target = clazz; 121 | while(!target.equals(Object.class)) { 122 | mMap = methodMap.get(target); 123 | if(mMap!=null) return mMap; 124 | target = target.getSuperclass(); 125 | } 126 | throw new IllegalArgumentException("No method map for delegate class [" + clazz.getName() + "]", new Throwable()); 127 | } 128 | 129 | /** 130 | * Creates a new BaseWrappedClass 131 | * @param delegate The attach API delegate object 132 | */ 133 | protected BaseWrappedClass(Object delegate) { 134 | this.delegate = delegate; 135 | if(methods==null) { 136 | synchronized(synchLock) { 137 | if(methods==null) { 138 | methods = getMethodMapping(this.getClass()); 139 | } 140 | } 141 | } 142 | } 143 | 144 | /** 145 | * Creates a new BaseWrappedClass with no delegate 146 | */ 147 | protected BaseWrappedClass() { 148 | this.delegate = null; 149 | methods = null; 150 | } 151 | 152 | 153 | /** 154 | * Returns the method mapping for the passed class 155 | * @param type The class to get the method mapping for 156 | * @return thge method map 157 | */ 158 | protected static Map getMethodMapping(Class type) { 159 | if(type==null) throw new IllegalArgumentException("The passed type was null", new Throwable()); 160 | Map mMap = methodMap.get(type); 161 | if(mMap==null) { 162 | synchronized(type) { 163 | mMap = methodMap.get(type); 164 | if(mMap==null) { 165 | Method[] methods = type.getDeclaredMethods(); 166 | mMap = new HashMap(methods.length); 167 | methodMap.put(type, mMap); 168 | Map overloads = mapOverloads(methods); 169 | for(Method m: methods) { 170 | m.setAccessible(true); 171 | String name = m.getName(); 172 | if(overloads.get(name)==1 || m.getParameterTypes().length==0) { 173 | mMap.put(name, m); 174 | } else { 175 | StringBuilder b = new StringBuilder(name); 176 | for(Class clazz: m.getParameterTypes()) { 177 | b.append(clazz.isPrimitive() ? clazz.getName().charAt(0) : clazz.getSimpleName().charAt(0)); 178 | } 179 | mMap.put(b.toString(), m); 180 | } 181 | } 182 | } 183 | } 184 | } 185 | return mMap; 186 | } 187 | 188 | 189 | /** 190 | * Returns a map of the method names and a count of the number of instances by each name 191 | * @param methods An array of methods 192 | * @return A map of overload counts keyed by method name 193 | */ 194 | protected static Map mapOverloads(Method[] methods) { 195 | Map map = new HashMap(methods.length); 196 | for(Method m: methods) { 197 | Integer i = map.get(m.getName()); 198 | if(i==null) { 199 | map.put(m.getName(), 1); 200 | } else { 201 | map.put(m.getName(), i+1); 202 | } 203 | } 204 | return map; 205 | } 206 | 207 | /** 208 | * Simple out formatted logger 209 | * @param fmt The format of the message 210 | * @param args The message arguments 211 | */ 212 | public void log(String fmt, Object...args) { 213 | // System.out.println(String.format(fmt, args)); 214 | log.info(String.format(fmt, args)); 215 | } 216 | 217 | /** 218 | * Simple err formatted logger 219 | * @param fmt The format of the message 220 | * @param args The message arguments 221 | */ 222 | public void loge(String fmt, Object...args) { 223 | //System.err.println(String.format(fmt, args)); 224 | log.severe(String.format(fmt, args)); 225 | } 226 | 227 | /** 228 | * Simple err formatted logger 229 | * @param fmt The format of the message 230 | * @param t The throwable to print stack trace for 231 | * @param args The message arguments 232 | */ 233 | public void loge(String fmt, Throwable t, Object...args) { 234 | log.log(Level.SEVERE, String.format(fmt, args), t); 235 | // System.err.println(String.format(fmt, args)); 236 | // t.printStackTrace(System.err); 237 | } 238 | 239 | 240 | 241 | 242 | } 243 | -------------------------------------------------------------------------------- /src/main/java/com/heliosapm/shorthand/attach/vm/VirtualMachine.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Helios, OpenSource Monitoring 3 | * Brought to you by the Helios Development Group 4 | * 5 | * Copyright 2007, Helios Development Group and individual contributors 6 | * as indicated by the @author tags. See the copyright.txt file in the 7 | * distribution for a full listing of individual contributors. 8 | * 9 | * This is free software; you can redistribute it and/or modify it 10 | * under the terms of the GNU Lesser General Public License as 11 | * published by the Free Software Foundation; either version 2.1 of 12 | * the License, or (at your option) any later version. 13 | * 14 | * This software is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | * Lesser General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Lesser General Public 20 | * License along with this software; if not, write to the Free 21 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 22 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 23 | * 24 | */ 25 | package com.heliosapm.shorthand.attach.vm; 26 | 27 | import java.io.File; 28 | import java.util.ArrayList; 29 | import java.util.List; 30 | import java.util.Map; 31 | import java.util.Properties; 32 | import java.util.concurrent.ConcurrentHashMap; 33 | 34 | import javax.management.MBeanServerConnection; 35 | import javax.management.remote.JMXConnector; 36 | import javax.management.remote.JMXConnectorFactory; 37 | import javax.management.remote.JMXServiceURL; 38 | 39 | /** 40 | *

Title: VirtualMachine

41 | *

Description: Wrapper class for Attach API's {@link com.sun.tools.attach.VirtualMachine}

42 | *

Company: Helios Development Group LLC

43 | * @author Whitehead (nwhitehead AT heliosdev DOT org) 44 | *

com.heliosapm.shorthand.attach.vm.VirtualMachine

45 | */ 46 | public class VirtualMachine extends BaseWrappedClass { 47 | /** A map of virtual machine keyed by their system identity hash codes */ 48 | private static final Map vmInstances = new ConcurrentHashMap(); 49 | /** The JMXServiceURL of this VirtualMachine */ 50 | private volatile JMXServiceURL jmxServiceURL = null; 51 | /** The agent property representing the JMXServiceURL of the management agent */ 52 | public static final String CONNECTOR_ADDRESS = "com.sun.management.jmxremote.localConnectorAddress"; 53 | /** The system property representing the file separator */ 54 | public static final String FILE_SEP = "file.separator"; 55 | /** The system property representing the JMX Remote Port */ 56 | public static final String JMX_PORT = "com.sun.management.jmxremote.port"; 57 | /** The JMX management agent jar name */ 58 | public static final String JMX_AGENT = "management-agent.jar"; 59 | /** The system property representing the java home */ 60 | public static final String JAVA_HOME = "java.home"; 61 | 62 | /** 63 | * Retrieves the VirtualMachine wrapper class for the passed VirtualMachine delegate 64 | * @param delegate the VirtualMachine delegate 65 | * @return the VirtualMachine 66 | */ 67 | public static VirtualMachine getInstance(Object delegate) { 68 | if(delegate==null) throw new IllegalArgumentException("The passed delegate was null", new Throwable()); 69 | if(!VirtualMachineBootstrap.getInstance().isInstanceOf(delegate, VirtualMachineBootstrap.VM_CLASS)) { 70 | throw new IllegalArgumentException("The passed delegate of type [" + delegate.getClass().getName() + "] is not an instance of [" + VirtualMachineBootstrap.VM_CLASS + "]", new Throwable()); 71 | } 72 | int key = System.identityHashCode(delegate); 73 | VirtualMachine vm = vmInstances.get(key); 74 | if(vm==null) { 75 | synchronized(vmInstances) { 76 | vm = vmInstances.get(key); 77 | if(vm==null) { 78 | vm = new VirtualMachine(delegate); 79 | vmInstances.put(key, vm); 80 | } 81 | } 82 | } 83 | return vm; 84 | } 85 | 86 | static { 87 | VirtualMachineBootstrap.getInstance(); 88 | } 89 | 90 | /** 91 | * Creates a new VirtualMachine wrapper instance 92 | * @param delegate The delegate 93 | */ 94 | VirtualMachine(Object delegate) { 95 | super(delegate); 96 | } 97 | 98 | /** 99 | * Tests this VirtualMachine for equality with another object. 100 | * @param obj The object to compare to 101 | * @return true if, and only if, the given object is a VirtualMachine that is equal to this VirtualMachine. 102 | */ 103 | @Override 104 | public boolean equals(Object obj){ 105 | if(obj==null) return false; 106 | return delegate.equals(obj); 107 | } 108 | 109 | /** 110 | * Returns the string representation of the VirtualMachine. 111 | * @return the string representation of the VirtualMachine. 112 | */ 113 | @Override 114 | public String toString(){ 115 | return delegate.toString(); 116 | } 117 | 118 | /** 119 | * Returns a hash-code value for this VirtualMachine. The hash code is based upon the VirtualMachine's components, and satifies the general contract of the Object.hashCode method. 120 | * @return A hash-code value for this virtual machine 121 | */ 122 | @Override 123 | public int hashCode(){ 124 | return delegate.hashCode(); 125 | } 126 | 127 | /** 128 | * Returns the provider that created this virtual machine. 129 | * @return The provider that created this virtual machine. 130 | */ 131 | public AttachProvider provider(){ 132 | try { 133 | pushCl(); 134 | return AttachProvider.getInstance( 135 | invoke(delegate, null, "provider") 136 | ); 137 | } finally { 138 | popCl(); 139 | } 140 | } 141 | 142 | /** 143 | * Return a list of Java virtual machines. 144 | * @return The list of virtual machine descriptors. 145 | */ 146 | public static List list(){ 147 | List list = new ArrayList(); 148 | try { 149 | pushCl(); 150 | List vmdDelegates = (List)invoke(null, VirtualMachineBootstrap.VM_CLASS, "list"); 151 | for(Object del: vmdDelegates) { 152 | list.add(VirtualMachineDescriptor.getInstance(del)); 153 | } 154 | return list; 155 | } finally { 156 | popCl(); 157 | } 158 | } 159 | 160 | /** 161 | * Returns the identifier for this Java virtual machine. 162 | * @return The identifier for this Java virtual machine. 163 | */ 164 | public String id() { 165 | try { 166 | pushCl(); 167 | return (String)invoke(delegate, null, "id"); 168 | } finally { 169 | popCl(); 170 | } 171 | } 172 | 173 | /** 174 | * Attaches to a Java virtual machine. 175 | * @param vmd The virtual machine descriptor. 176 | * @return A VirtualMachine representing the target VM. 177 | */ 178 | public static VirtualMachine attach(VirtualMachineDescriptor vmd) { 179 | if(vmd==null) throw new IllegalArgumentException("The passed VirtualMachineDescriptor was null", new Throwable()); 180 | try { 181 | pushCl(); 182 | Object vmDelegate = invoke(null, VirtualMachineBootstrap.VM_CLASS, "attachV", vmd.delegate); 183 | return new VirtualMachine(vmDelegate); 184 | } catch (Exception e) { 185 | throw new RuntimeException("Failed to attach to VirtualMachine [" + vmd.toString() + "]", e); 186 | } finally { 187 | popCl(); 188 | } 189 | } 190 | 191 | /** 192 | * Attaches to a Java virtual machine. 193 | * @param id The abstract identifier that identifies the Java virtual machine. 194 | * @return A VirtualMachine representing the target VM. 195 | */ 196 | public static VirtualMachine attach(String id) { 197 | if(id==null) throw new IllegalArgumentException("The passed VirtualMachine id was null", new Throwable()); 198 | try { 199 | pushCl(); 200 | Object vmDelegate = invoke(null, VirtualMachineBootstrap.VM_CLASS, "attachS", id); 201 | return new VirtualMachine(vmDelegate); 202 | } catch (Exception e) { 203 | throw new RuntimeException("Failed to attach to VirtualMachine [" + id + "]", e); 204 | } finally { 205 | popCl(); 206 | } 207 | } 208 | 209 | /** 210 | * Detach from the virtual machine. 211 | */ 212 | public void detach() { 213 | try { 214 | pushCl(); 215 | invoke(delegate, null, "detach"); 216 | } finally { 217 | popCl(); 218 | } 219 | } 220 | 221 | /** 222 | * Returns the current agent properties in the target virtual machine. 223 | * @return The agent properties 224 | */ 225 | public Properties getAgentProperties() { 226 | try { 227 | pushCl(); 228 | return (Properties)invoke(delegate, null, "getAgentProperties"); 229 | } finally { 230 | popCl(); 231 | } 232 | } 233 | 234 | /** 235 | * Returns the current system properties in the target virtual machine. 236 | * @return The system properties 237 | */ 238 | public Properties getSystemProperties() { 239 | try { 240 | pushCl(); 241 | return (Properties)invoke(delegate, null, "getSystemProperties"); 242 | } finally { 243 | popCl(); 244 | } 245 | } 246 | 247 | /** 248 | * Loads an agent. 249 | * @param agent Path to the JAR file containing the agent. 250 | */ 251 | public void loadAgent(String agent) { 252 | try { 253 | pushCl(); 254 | invoke(delegate, null, "loadAgentS", agent); 255 | } finally { 256 | popCl(); 257 | } 258 | } 259 | 260 | /** 261 | * Loads an agent. 262 | * @param agent Path to the JAR file containing the agent. 263 | * @param options The options to provide to the agent's agentmain method (can be null). 264 | */ 265 | public void loadAgent(String agent, String options) { 266 | try { 267 | pushCl(); 268 | invoke(delegate, null, "loadAgentSS", agent, options); 269 | } finally { 270 | popCl(); 271 | } 272 | } 273 | 274 | /** 275 | * Loads an agent library. 276 | * @param agentLibrary The name of the agent library. 277 | */ 278 | public void loadAgentLibrary(String agentLibrary) { 279 | try { 280 | pushCl(); 281 | invoke(delegate, null, "loadAgentLibraryS", agentLibrary); 282 | } finally { 283 | popCl(); 284 | } 285 | } 286 | 287 | /** 288 | * Loads an agent library. 289 | * @param agentLibrary The name of the agent library. 290 | * @param options The options to provide to the Agent_OnAttach function (can be null). 291 | */ 292 | public void loadAgentLibrary(String agentLibrary, String options) { 293 | try { 294 | pushCl(); 295 | invoke(delegate, null, "loadAgentLibrarySS", agentLibrary, options); 296 | } finally { 297 | popCl(); 298 | } 299 | } 300 | 301 | /** 302 | * Load a native agent library by full pathname. 303 | * @param agentPath The full path to the agent library. 304 | */ 305 | public void loadAgentPath(String agentPath) { 306 | try { 307 | pushCl(); 308 | invoke(delegate, null, "loadAgentPathS", agentPath); 309 | } finally { 310 | popCl(); 311 | } 312 | } 313 | 314 | /** 315 | * Load a native agent library by full pathname. 316 | * @param agentPath The full path to the agent library. 317 | * @param options The options to provide to the Agent_OnAttach function (can be null). 318 | */ 319 | public void loadAgentPath(String agentPath, String options) { 320 | try { 321 | pushCl(); 322 | invoke(delegate, null, "loadAgentPathSS", agentPath, options); 323 | } finally { 324 | popCl(); 325 | } 326 | } 327 | 328 | /** 329 | * Returns a {@link MBeanServerConnection} to this VM instance 330 | * @return a {@link MBeanServerConnection} to this VM instance 331 | */ 332 | public MBeanServerConnection getMBeanServerConnection() { 333 | try { 334 | return getJMXConnector().getMBeanServerConnection(); 335 | } catch (Exception e) { 336 | throw new RuntimeException("Failed to acquire MBeanServerConnection from VirtualMachine [" + id() + "]", e); 337 | } 338 | } 339 | 340 | 341 | /** 342 | * Returns a {@link JMXConnector} to this VM instance 343 | * @return a {@link JMXConnector} to this VM instance 344 | */ 345 | public JMXConnector getJMXConnector() { 346 | try { 347 | JMXServiceURL serviceURL = getJMXServiceURL(); 348 | return JMXConnectorFactory.connect(serviceURL); 349 | } catch (Exception e) { 350 | throw new RuntimeException("Failed to acquire JMXConnector from VirtualMachine [" + id() + "]", e); 351 | } 352 | } 353 | 354 | /** 355 | * Returns a {@link JMXServiceURL} to connect to this VM instance 356 | * @return a {@link JMXServiceURL} to connect to this VM instance 357 | * TODO: We need to allow this using authentication. 358 | */ 359 | public JMXServiceURL getJMXServiceURL() { 360 | if(jmxServiceURL==null) { 361 | synchronized(this) { 362 | if(jmxServiceURL==null) { 363 | try { 364 | String connAddr = getAgentProperties().getProperty(CONNECTOR_ADDRESS); 365 | if(connAddr==null) { 366 | Properties sysProps = getSystemProperties(); 367 | String fileSep = sysProps.getProperty(FILE_SEP, File.separator); 368 | String javaHome = sysProps.getProperty(JAVA_HOME); 369 | String agentPath = String.format("%s%slib%s%s", javaHome, fileSep, fileSep, JMX_AGENT); 370 | loadAgent(agentPath); 371 | //, JMX_PORT + "=" + FreePortFinder.getNextFreePort() + ",com.sun.management.jmxremote.authenticate=false"); 372 | connAddr = getAgentProperties().getProperty(CONNECTOR_ADDRESS); 373 | } 374 | if(connAddr==null) throw new RuntimeException("Failed to acquire JMXServiceURL for MBeanServerConnection to VirtualMachine [" + id() + "]", new Throwable()); 375 | jmxServiceURL = new JMXServiceURL(connAddr); 376 | } catch (Exception e) { 377 | throw new RuntimeException("Failed to acquire JMXServiceURL from VirtualMachine [" + id() + "]", e); 378 | } 379 | 380 | } 381 | } 382 | } 383 | return jmxServiceURL; 384 | } 385 | 386 | } 387 | 388 | -------------------------------------------------------------------------------- /src/main/java/com/heliosapm/shorthand/attach/vm/VirtualMachineBootstrap.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Helios, OpenSource Monitoring 3 | * Brought to you by the Helios Development Group 4 | * 5 | * Copyright 2007, Helios Development Group and individual contributors 6 | * as indicated by the @author tags. See the copyright.txt file in the 7 | * distribution for a full listing of individual contributors. 8 | * 9 | * This is free software; you can redistribute it and/or modify it 10 | * under the terms of the GNU Lesser General Public License as 11 | * published by the Free Software Foundation; either version 2.1 of 12 | * the License, or (at your option) any later version. 13 | * 14 | * This software is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | * Lesser General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Lesser General Public 20 | * License along with this software; if not, write to the Free 21 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 22 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 23 | * 24 | */ 25 | package com.heliosapm.shorthand.attach.vm; 26 | 27 | import java.io.File; 28 | import java.lang.management.ManagementFactory; 29 | import java.net.URL; 30 | import java.net.URLClassLoader; 31 | import java.util.ArrayList; 32 | import java.util.HashMap; 33 | import java.util.List; 34 | import java.util.Map; 35 | import java.util.Properties; 36 | import java.util.concurrent.atomic.AtomicBoolean; 37 | import java.util.concurrent.atomic.AtomicReference; 38 | import java.util.logging.Logger; 39 | 40 | /** 41 | *

Title: VirtualMachineBootstrap

42 | *

Description: Bootstraps the Java Attach API class set

43 | *

Company: Helios Development Group LLC

44 | * @author Whitehead (nwhitehead AT heliosdev DOT org) 45 | *

com.heliosapm.shorthand.attach.vm.VirtualMachineBootstrap

46 | */ 47 | public class VirtualMachineBootstrap extends BaseWrappedClass { 48 | /** The singleton instance */ 49 | protected static volatile VirtualMachineBootstrap instance = null; 50 | /** The singleton instance ctor lock */ 51 | protected static final Object lock = new Object(); 52 | /** The class loader used to find the Attach API Jar */ 53 | protected static final AtomicReference attachClassLoader = new AtomicReference(null); 54 | /** Static class logger */ 55 | private final static Logger log = Logger.getLogger(VirtualMachineBootstrap.class.getName()); 56 | 57 | 58 | 59 | /** A cache of the reflectively loaded classes keyed by class name */ 60 | protected final Map> classCache = new HashMap>(); 61 | 62 | /** The jar file that usually contains the attach API VirtualMachine classes */ 63 | public static final String JAR_NAME = "tools.jar"; 64 | /** The Java Home location */ 65 | public static final String JAVA_HOME = System.getProperty("java.home"); 66 | /** Alternate locations to look for tools.jar */ 67 | public static final String[] ALT_LOCS = { 68 | File.separator + ".." + File.separator + "lib" + File.separator 69 | }; 70 | 71 | /** Flag indicating if the attach classes have been found */ 72 | private static final AtomicBoolean found = new AtomicBoolean(false); 73 | 74 | 75 | /** The attach API VirtualMachine class name */ 76 | public static final String VM_CLASS = "com.sun.tools.attach.VirtualMachine"; 77 | /** The attach API VirtualMachineDescriptor class name */ 78 | public static final String VM_DESC_CLASS = "com.sun.tools.attach.VirtualMachineDescriptor"; 79 | /** The attach API AttachProvider class name */ 80 | public static final String ATTACH_PROVIDER_CLASS = "com.sun.tools.attach.spi.AttachProvider"; 81 | 82 | 83 | 84 | /** 85 | * Returns the VirtualMachineBootstrap instance 86 | * @return the VirtualMachineBootstrap instance 87 | */ 88 | public static VirtualMachineBootstrap getInstance() { 89 | return getInstance(null); 90 | } 91 | 92 | 93 | 94 | 95 | /** 96 | * Returns the VirtualMachineBootstrap instance 97 | * @param urlLocation An optional override fully qualified URL of the attach jar 98 | * @return the VirtualMachineBootstrap instance 99 | */ 100 | public static VirtualMachineBootstrap getInstance(String urlLocation) { 101 | if(instance==null) { 102 | synchronized(lock) { 103 | if(instance==null) { 104 | instance = new VirtualMachineBootstrap(urlLocation); 105 | try { 106 | AttachProvider.init(); 107 | } catch (Exception e) { 108 | e.printStackTrace(System.err); 109 | throw new RuntimeException("Failed to load Attach API Class. (If you are running a JRE, you need to use a JDK with a tools.jar", e); 110 | } 111 | } 112 | } 113 | } 114 | return instance; 115 | } 116 | 117 | /** 118 | * Creates a new VirtualMachineBootstrap 119 | * @param urlLocation An optional override fully qualified URL of the attach jar 120 | */ 121 | protected VirtualMachineBootstrap(String urlLocation) { 122 | 123 | findAttachAPI(urlLocation); 124 | ClassLoader cl = attachClassLoader.get(); 125 | try { 126 | classCache.put(VM_CLASS, Class.forName(VM_CLASS, true, cl)); 127 | classCache.put(VM_DESC_CLASS, Class.forName(VM_DESC_CLASS, true, cl)); 128 | classCache.put(ATTACH_PROVIDER_CLASS, Class.forName(ATTACH_PROVIDER_CLASS, true, cl)); 129 | BaseWrappedClass.getMethodMapping(classCache.get(VM_CLASS)); 130 | BaseWrappedClass.getMethodMapping(classCache.get(VM_DESC_CLASS)); 131 | BaseWrappedClass.getMethodMapping(classCache.get(ATTACH_PROVIDER_CLASS)); 132 | } catch (Exception e) { 133 | e.printStackTrace(System.err); 134 | throw new RuntimeException("Failed to load Attach API Class. (If you are running a JRE, you need to use a JDK with a tools.jar", e); 135 | } 136 | } 137 | 138 | /** 139 | * Determines if the passed delegate object is an Attach API class instance and if it is an instance of the named class. 140 | * @param obj The delegate object to pass 141 | * @param className The class name to test as 142 | * @return true if the object is of the passed class. 143 | */ 144 | public boolean isInstanceOf(Object obj, String className) { 145 | if(obj==null) throw new IllegalArgumentException("The passed delegate object was null", new Throwable()); 146 | if(className==null) throw new IllegalArgumentException("The passed class name was null", new Throwable()); 147 | Class clazz = classCache.get(className); 148 | if(clazz==null) throw new IllegalArgumentException("The passed class name [" + className + "] is not an Attach API class", new Throwable()); 149 | return clazz.isAssignableFrom(obj.getClass()); 150 | } 151 | 152 | 153 | 154 | /** 155 | * Indicates if the Attach VirtualMachine class can be found in the current classpath 156 | * @return true if the Attach VirtualMachine class can be loaded, false if it cannot. 157 | */ 158 | protected static boolean inClassPath() { 159 | return inClassPath(Thread.currentThread().getContextClassLoader()); 160 | } 161 | 162 | /** 163 | * Indicates if the attach classes have been found 164 | * @return true if the attach classes have been found, false otherwise 165 | */ 166 | public static boolean isAttachFound() { 167 | return found.get(); 168 | } 169 | 170 | /** 171 | * Indicates if the Attach VirtualMachine class can be found in the current classpath 172 | * @param classLoader The class loader used to find the Attach API Jar 173 | * @return true if the Attach VirtualMachine class can be loaded, false if it cannot. 174 | */ 175 | protected static boolean inClassPath(ClassLoader classLoader) { 176 | if(attachClassLoader.get()!=null) return true; 177 | try { 178 | Class clazz = Class.forName(VM_CLASS, true, ClassLoader.getSystemClassLoader()); 179 | attachClassLoader.set(clazz.getClassLoader()==null ? classLoader : clazz.getClassLoader()); 180 | found.set(true); 181 | } catch (Exception e) { 182 | // e.printStackTrace(System.err); 183 | } 184 | try { 185 | Class clazz = Class.forName(VM_CLASS, true, classLoader); 186 | attachClassLoader.set(clazz.getClassLoader()==null ? classLoader : clazz.getClassLoader()); 187 | found.set(true); 188 | return true; 189 | } catch (Exception e) { 190 | return false; 191 | } 192 | } 193 | 194 | /** 195 | * Searches for the Attach API jar 196 | */ 197 | protected static void findAttachAPI() { 198 | findAttachAPI(null); 199 | } 200 | 201 | /** 202 | * Searches for the Attach API jar 203 | * @param urlLocation An optional override fully qualified URL of the attach jar 204 | */ 205 | protected static void findAttachAPI(String urlLocation) { 206 | if(found.get()) return; 207 | //if(inClassPath()) return; 208 | try { 209 | Class clazz = Class.forName(VM_CLASS); 210 | // log.info("Found AttachAPI in Standard ClassPath [" + clazz.getClassLoader() + "]"); 211 | ClassLoader cl = clazz.getClassLoader(); 212 | if(cl==null) { 213 | cl = ClassLoader.getSystemClassLoader(); 214 | } 215 | // log.info("Attach API ClassLoader:" + cl); 216 | attachClassLoader.set(cl); 217 | found.set(true); 218 | BaseWrappedClass.savedState.set(null); 219 | return; 220 | } catch (Throwable e) { 221 | // log.info("Not found:" + e); 222 | } 223 | List altLocs = new ArrayList(); 224 | if(urlLocation!=null) { 225 | altLocs.add(urlLocation); 226 | } 227 | for(String s: ALT_LOCS) { 228 | // System.out.println("ALT_LOC: [" + (JAVA_HOME + s + JAR_NAME) + "]"); 229 | altLocs.add(JAVA_HOME + s + JAR_NAME); 230 | } 231 | for(String s: altLocs) { 232 | try { 233 | File toolsLoc = new File(s); 234 | //log.info("Testing [" + toolsLoc + "]"); 235 | if(toolsLoc.exists()) { 236 | URL url = toolsLoc.toURI().toURL(); 237 | URLClassLoader ucl = new URLClassLoader(new URL[]{url}, ClassLoader.getSystemClassLoader().getParent()); 238 | if(inClassPath(ucl)) { 239 | //log.info("Attach API Found And Loaded [" + toolsLoc + "]"); 240 | // attachClassLoader.set(ucl); 241 | // BaseWrappedClass.savedState.set(null); 242 | return; 243 | } 244 | } 245 | } catch (Exception e) { 246 | } 247 | } 248 | if(attachClassLoader.get()==null) { 249 | throw new RuntimeException("Failed to find the Attach API. Please add tools.jar to the classpath", new Throwable()); 250 | } 251 | } 252 | 253 | /** 254 | * Quickie command line test 255 | * @param args None 256 | */ 257 | public static void main(String[] args) { 258 | findAttachAPI(); 259 | log.info("VMBoot:" + inClassPath()); 260 | getInstance(); 261 | // for(VirtualMachineDescriptor vmd: VirtualMachine.list()) { 262 | // try { 263 | // VirtualMachine vm = VirtualMachine.attach(vmd); 264 | // log.info("\tVM:" + vm.toString()); 265 | // } catch (Exception e) { 266 | // log.info("Unable to attach to VM [" + vmd.toString() + "]:" + e); 267 | // } 268 | // } 269 | // for(VirtualMachineDescriptor vmd: VirtualMachineDescriptor.getVirtualMachineDescriptors()) { 270 | // try { 271 | // VirtualMachine vm = VirtualMachine.attach(vmd); 272 | // log.info("\tVM:" + vm.toString()); 273 | // } catch (Exception e) { 274 | // log.info("Unable to attach to VM [" + vmd.toString() + "]:" + e); 275 | // } 276 | // } 277 | // AttachProvider ap = AttachProvider.getAttachProviders().iterator().next(); 278 | // String id = ManagementFactory.getRuntimeMXBean().getName().split("@")[0]; 279 | // VirtualMachine vm = ap.attachVirtualMachine(id); 280 | // log.info("This VM:" + vm.toString()); 281 | AttachProvider ap = AttachProvider.getAttachProviders().iterator().next(); 282 | String id = ManagementFactory.getRuntimeMXBean().getName().split("@")[0]; 283 | for(VirtualMachineDescriptor vmd: ap.listVirtualMachines()) { 284 | log.info("Testing VMD (" + vmd.id() + ") [" + vmd.displayName() + "] Name:" + vmd.provider().name() + " Type:" + vmd.provider().type()); 285 | if(id.equals(vmd.id())) { 286 | VirtualMachine vm = ap.attachVirtualMachine(vmd); 287 | log.info("This VM:" + vm.toString()); 288 | Properties agentProps = vm.getAgentProperties(); 289 | for(Map.Entry p: agentProps.entrySet()) { 290 | log.info("\t\t" + p.getKey() + ":" + p.getValue()); 291 | } 292 | } 293 | if(vmd.id().equals("15684")) { 294 | // log.info("============== System Props =============="); 295 | // Properties sysProps = ap.attachVirtualMachine(vmd).getSystemProperties(); 296 | // for(Map.Entry p: sysProps.entrySet()) { 297 | // log.info("\t\t" + p.getKey() + ":" + p.getValue()); 298 | // } 299 | log.info("============== Agent Props =============="); 300 | Properties agentProps = ap.attachVirtualMachine(vmd).getAgentProperties(); 301 | for(Map.Entry p: agentProps.entrySet()) { 302 | log.info("\t\t" + p.getKey() + ":" + p.getValue()); 303 | } 304 | 305 | } 306 | 307 | } 308 | 309 | // BaseWrappedClass.getMethodMapping(vmb.classCache.get(VM_CLASS)); 310 | // for(VirtualMachineDescriptor vmd: VirtualMachine.list()) { 311 | // log.info(vmd.toString()); 312 | // } 313 | 314 | } 315 | 316 | } 317 | -------------------------------------------------------------------------------- /src/main/java/com/heliosapm/shorthand/attach/vm/VirtualMachineDescriptor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Helios, OpenSource Monitoring 3 | * Brought to you by the Helios Development Group 4 | * 5 | * Copyright 2007, Helios Development Group and individual contributors 6 | * as indicated by the @author tags. See the copyright.txt file in the 7 | * distribution for a full listing of individual contributors. 8 | * 9 | * This is free software; you can redistribute it and/or modify it 10 | * under the terms of the GNU Lesser General Public License as 11 | * published by the Free Software Foundation; either version 2.1 of 12 | * the License, or (at your option) any later version. 13 | * 14 | * This software is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | * Lesser General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Lesser General Public 20 | * License along with this software; if not, write to the Free 21 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 22 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 23 | * 24 | */ 25 | package com.heliosapm.shorthand.attach.vm; 26 | 27 | import java.util.ArrayList; 28 | import java.util.List; 29 | import java.util.Map; 30 | import java.util.concurrent.ConcurrentHashMap; 31 | 32 | /** 33 | *

Title: VirtualMachineDescriptor

34 | *

Description: Wrapper class for Attach API's {@link com.sun.tools.attach.VirtualMachineDescriptor}

35 | *

Company: Helios Development Group LLC

36 | * @author Whitehead (nwhitehead AT heliosdev DOT org) 37 | *

com.heliosapm.shorthand.attach.vm.VirtualMachineDescriptor

38 | */ 39 | public class VirtualMachineDescriptor extends BaseWrappedClass { 40 | /** A map of machine descriptors delegates keyed by their system identity hash codes */ 41 | private static final Map vmdInstances = new ConcurrentHashMap(); 42 | 43 | /** 44 | * Acquires the wrapped VirtualMachineDescriptor for the passed delegate 45 | * @param delegate The VirtualMachineDescriptor delegate object 46 | * @return a wrapped VirtualMachineDescriptor 47 | */ 48 | public static VirtualMachineDescriptor getInstance(Object delegate) { 49 | if(delegate==null) throw new IllegalArgumentException("The passed VirtualMachineDescriptor delegate was null", new Throwable()); 50 | if(!VirtualMachineBootstrap.getInstance().isInstanceOf(delegate, VirtualMachineBootstrap.VM_DESC_CLASS)) { 51 | throw new IllegalArgumentException("The passed delegate of type [" + delegate.getClass().getName() + "] was not of the type [" + VirtualMachineBootstrap.VM_DESC_CLASS + "]", new Throwable()); 52 | } 53 | int id = System.identityHashCode(delegate); 54 | VirtualMachineDescriptor vmd = vmdInstances.get(id); 55 | if(vmd==null) { 56 | synchronized(vmdInstances) { 57 | vmd = vmdInstances.get(id); 58 | if(vmd==null) { 59 | vmd = new VirtualMachineDescriptor(delegate); 60 | vmdInstances.put(id, vmd); 61 | } 62 | } 63 | } 64 | return vmd; 65 | } 66 | 67 | /** 68 | * Returns a list of all registered VirtualMachineDescriptors 69 | * @return a list of all registered VirtualMachineDescriptors 70 | */ 71 | public static List getVirtualMachineDescriptors() { 72 | List results = new ArrayList(); 73 | try { 74 | pushCl(); 75 | for(AttachProvider ap: AttachProvider.getAttachProviders()) { 76 | for(VirtualMachineDescriptor vmd: ap.listVirtualMachines()) { 77 | results.add(vmd); 78 | int key = System.identityHashCode(vmd.delegate); 79 | if(!vmdInstances.containsKey(key)) { 80 | synchronized(vmdInstances) { 81 | if(!vmdInstances.containsKey(key)) { 82 | VirtualMachineDescriptor virtualMachineDescriptor = new VirtualMachineDescriptor(vmd); 83 | vmdInstances.put(key, virtualMachineDescriptor); 84 | } 85 | } 86 | } 87 | } 88 | } 89 | return results; 90 | } catch (Exception e) { 91 | throw new RuntimeException("Failed to list all VirtualMachineDescriptors", e); 92 | } finally { 93 | popCl(); 94 | } 95 | } 96 | 97 | /** 98 | * Return the identifier component of this descriptor. 99 | * @return The identifier component of this descriptor. 100 | */ 101 | public String id() { 102 | try { 103 | pushCl(); 104 | return (String)invoke(delegate, null, "id"); 105 | } finally { 106 | popCl(); 107 | } 108 | } 109 | 110 | /** 111 | * Return the display name component of this descriptor. 112 | * @return The display name component of this descriptor. 113 | */ 114 | public String displayName() { 115 | try { 116 | pushCl(); 117 | return (String)invoke(delegate, null, "displayName"); 118 | } finally { 119 | popCl(); 120 | } 121 | } 122 | 123 | /** 124 | * Return the AttachProvider that this descriptor references. 125 | * @return The AttachProvider that this descriptor references. 126 | */ 127 | public AttachProvider provider() { 128 | try { 129 | pushCl(); 130 | return AttachProvider.getInstance(invoke(delegate, null, "provider")); 131 | } finally { 132 | popCl(); 133 | } 134 | } 135 | 136 | 137 | /** 138 | * Tests this VirtualMachineDescriptor for equality with another object. 139 | * @param obj The object to compare to 140 | * @return true if, and only if, the given object is a VirtualMachineDescriptor that is equal to this VirtualMachine. 141 | */ 142 | @Override 143 | public boolean equals(Object obj){ 144 | if(obj==null) return false; 145 | return delegate.equals(obj); 146 | } 147 | 148 | /** 149 | * Returns a hash-code value for this VirtualMachineDescriptor. The hash code is based upon the VirtualMachine's components, and satifies the general contract of the Object.hashCode method. 150 | * @return A hash-code value for this VirtualMachineDescriptor 151 | */ 152 | @Override 153 | public int hashCode(){ 154 | return delegate.hashCode(); 155 | } 156 | 157 | /** 158 | * Creates a new VirtualMachineDescriptor 159 | * @param delegate The VirtualMachineDescriptor delegate object 160 | */ 161 | private VirtualMachineDescriptor(Object delegate) { 162 | super(delegate); 163 | } 164 | 165 | 166 | @Override 167 | public String toString() { 168 | return delegate.toString(); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/main/java/com/heliosapm/shorthand/attach/vm/VirtualMachineInvocationException.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Helios, OpenSource Monitoring 3 | * Brought to you by the Helios Development Group 4 | * 5 | * Copyright 2007, Helios Development Group and individual contributors 6 | * as indicated by the @author tags. See the copyright.txt file in the 7 | * distribution for a full listing of individual contributors. 8 | * 9 | * This is free software; you can redistribute it and/or modify it 10 | * under the terms of the GNU Lesser General Public License as 11 | * published by the Free Software Foundation; either version 2.1 of 12 | * the License, or (at your option) any later version. 13 | * 14 | * This software is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | * Lesser General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Lesser General Public 20 | * License along with this software; if not, write to the Free 21 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 22 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 23 | * 24 | */ 25 | package com.heliosapm.shorthand.attach.vm; 26 | 27 | import java.io.ObjectStreamException; 28 | 29 | /** 30 | *

Title: VirtualMachineInvocationException

31 | *

Description: Exception thrown when a reflective invocation against an attached [@link VirtualMachine} fails

32 | *

Company: Helios Development Group LLC

33 | * @author Whitehead (nwhitehead AT heliosdev DOT org) 34 | *

com.heliosapm.shorthand.attach.vm.VirtualMachineInvocationException

35 | */ 36 | public class VirtualMachineInvocationException extends RuntimeException { 37 | 38 | /** */ 39 | private static final long serialVersionUID = 8560019290540847249L; 40 | 41 | /** 42 | * Creates a new VirtualMachineInvocationException 43 | * @param message The exception message 44 | * @param cause the underlying cause of the exception 45 | */ 46 | public VirtualMachineInvocationException(String message, Throwable cause) { 47 | super(message, cause); 48 | } 49 | 50 | /** 51 | * Replaces this class with a standard {@link RuntimeException} when serializing 52 | * since the receiving end may not have this class in its classpath 53 | * @return a standard {@link RuntimeException} 54 | * @throws ObjectStreamException 55 | */ 56 | Object writeReplace() throws ObjectStreamException { 57 | return new RuntimeException("[Converted VirtualMachineInvocationException]" + this.getMessage(), this.getCause()); 58 | } 59 | 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/main/resources/defaultconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | com.heliosapm.jvmti.extension.impls.DirectByteBufferAllocations 5 | com.heliosapm.jvmti.extension.impls.HotspotExtension 6 | com.heliosapm.jvmti.extension.impls.thread.ThreadPoolMonitor 7 | 8 | 9 | ${jmxmp.port:2071} 10 | 0.0.0.0 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/main/resources/js/jmx.js: -------------------------------------------------------------------------------- 1 | var JMXHelper = Java.type('com.heliosapm.utils.jmx.JMXHelper'); 2 | -------------------------------------------------------------------------------- /src/main/resources/tinylog.properties: -------------------------------------------------------------------------------- 1 | tinylog.writer1=console 2 | tinylog.writer1.format={date:yyyy-MM-dd HH:mm:ss} [{thread}] {class_name}.{method}() {level}: {message} 3 | tinylog.level=info -------------------------------------------------------------------------------- /src/test/java/com/heliosapm/jvmti/BaseTest.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Helios, OpenSource Monitoring 3 | * Brought to you by the Helios Development Group 4 | * 5 | * Copyright 2016, Helios Development Group and individual contributors 6 | * as indicated by the @author tags. See the copyright.txt file in the 7 | * distribution for a full listing of individual contributors. 8 | * 9 | * This is free software; you can redistribute it and/or modify it 10 | * under the terms of the GNU Lesser General Public License as 11 | * published by the Free Software Foundation; either version 2.1 of 12 | * the License, or (at your option) any later version. 13 | * 14 | * This software is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 | * Lesser General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Lesser General Public 20 | * License along with this software; if not, write to the Free 21 | * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 22 | * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 23 | * 24 | */ 25 | package com.heliosapm.jvmti; 26 | 27 | import java.io.File; 28 | import java.io.PrintStream; 29 | import java.util.HashMap; 30 | import java.util.LinkedHashMap; 31 | import java.util.Map; 32 | import java.util.Random; 33 | import java.util.Set; 34 | import java.util.UUID; 35 | import java.util.concurrent.CopyOnWriteArraySet; 36 | import java.util.concurrent.Executors; 37 | import java.util.concurrent.ScheduledExecutorService; 38 | import java.util.concurrent.ThreadFactory; 39 | import java.util.concurrent.atomic.AtomicInteger; 40 | import java.util.concurrent.atomic.AtomicLong; 41 | 42 | import org.junit.After; 43 | import org.junit.Before; 44 | import org.junit.BeforeClass; 45 | import org.junit.Ignore; 46 | import org.junit.Rule; 47 | import org.junit.rules.TestName; 48 | 49 | 50 | /** 51 | *

Title: BaseTest

52 | *

Description: Base class for unit tests

53 | *

Company: Helios Development Group LLC

54 | * @author Whitehead (nwhitehead AT heliosdev DOT org) 55 | *

com.heliosapm.jvmti.BaseTest

56 | */ 57 | @Ignore 58 | public class BaseTest { 59 | /** The currently executing test name */ 60 | @Rule public final TestName name = new TestName(); 61 | /** A random value generator */ 62 | protected static final Random RANDOM = new Random(System.currentTimeMillis()); 63 | 64 | /** Retain system out */ 65 | protected static final PrintStream OUT = System.out; 66 | /** Retain system err */ 67 | protected static final PrintStream ERR = System.err; 68 | 69 | /** Synthetic UIDMeta counter for metrics */ 70 | protected static final AtomicInteger METRIC_COUNTER = new AtomicInteger(); 71 | /** Synthetic UIDMeta counter for tag keys */ 72 | protected static final AtomicInteger TAGK_COUNTER = new AtomicInteger(); 73 | /** Synthetic UIDMeta counter for tag values */ 74 | protected static final AtomicInteger TAGV_COUNTER = new AtomicInteger(); 75 | 76 | 77 | 78 | /** A shared testing scheduler */ 79 | protected static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2, new ThreadFactory(){ 80 | final AtomicInteger serial = new AtomicInteger(); 81 | @Override 82 | public Thread newThread(Runnable r) { 83 | Thread t = new Thread(r, "BaseTestScheduler#" + serial.incrementAndGet()); 84 | t.setDaemon(true); 85 | return t; 86 | } 87 | }); 88 | 89 | 90 | 91 | 92 | 93 | /** 94 | * Returns a random positive long 95 | * @return a random positive long 96 | */ 97 | public static long nextPosLong() { 98 | return Math.abs(RANDOM.nextLong()); 99 | } 100 | 101 | /** 102 | * Returns a random positive double 103 | * @return a random positive double 104 | */ 105 | public static double nextPosDouble() { 106 | return Math.abs(RANDOM.nextDouble()); 107 | } 108 | 109 | /** 110 | * Returns a random boolean 111 | * @return a random boolean 112 | */ 113 | public static boolean nextBoolean() { 114 | return RANDOM.nextBoolean(); 115 | } 116 | 117 | /** 118 | * Returns a random positive int 119 | * @return a random positive int 120 | */ 121 | public static int nextPosInt() { 122 | return Math.abs(RANDOM.nextInt()); 123 | } 124 | /** 125 | * Returns a random positive int within the bound 126 | * @param bound the bound on the random number to be returned. Must be positive. 127 | * @return a random positive int 128 | */ 129 | public static int nextPosInt(int bound) { 130 | return Math.abs(RANDOM.nextInt(bound)); 131 | } 132 | 133 | 134 | 135 | /** 136 | * Prints the test name about to be executed 137 | */ 138 | @Before 139 | public void printTestName() { 140 | log("\n\t==================================\n\tRunning Test [" + name.getMethodName() + "]\n\t==================================\n"); 141 | } 142 | 143 | 144 | 145 | @After 146 | public void printTestEnd() { 147 | 148 | } 149 | 150 | 151 | /** 152 | * Stalls the calling thread 153 | * @param time the time to stall in ms. 154 | */ 155 | public static void sleep(final long time) { 156 | try { 157 | Thread.currentThread().join(time); 158 | } catch (Exception ex) { 159 | throw new RuntimeException(ex); 160 | } 161 | } 162 | 163 | 164 | 165 | 166 | public static void deleteDir(File dir) { 167 | // delete one level. 168 | if (dir.isDirectory()) { 169 | File[] files = dir.listFiles(); 170 | if (files != null) 171 | for (File file : files) 172 | if (file.isDirectory()) { 173 | deleteDir(file); 174 | 175 | } else if (!file.delete()) { 176 | System.err.println("... unable to delete [" + file + "]"); 177 | } 178 | } 179 | dir.delete(); 180 | } 181 | 182 | public static void deleteDir(String dir) { 183 | final File f = new File(dir); 184 | if(f.exists() && f.isDirectory()) { 185 | deleteDir(f); 186 | } 187 | } 188 | 189 | 190 | 191 | 192 | 193 | /** 194 | * Compares two string maps for equality where both being null means null 195 | * @param a One string map 196 | * @param b Another string map 197 | * @return true if equal, false otherwise 198 | */ 199 | public static boolean equal(final Map a, final Map b) { 200 | if(a==null && b==null) return true; 201 | if(a==null || b==null) return false; 202 | if(a.size() != b.size()) return false; 203 | if(a.isEmpty()==b.isEmpty()) return true; 204 | for(Map.Entry entry: a.entrySet()) { 205 | String akey = entry.getKey(); 206 | String avalue = entry.getValue(); 207 | String bvalue = b.get(akey); 208 | if(bvalue==null) return false; 209 | if(!equal(avalue, bvalue)) return false; 210 | } 211 | return true; 212 | 213 | } 214 | 215 | /** 216 | * Compares two strings for equality where both being null means null 217 | * @param a One string 218 | * @param b Another string 219 | * @return true if equal, false otherwise 220 | */ 221 | public static boolean equal(final String a, final String b) { 222 | if(a==null && b==null) return true; 223 | if(a==null || b==null) return false; 224 | return a.equals(b); 225 | } 226 | 227 | /** 228 | * Creates a map of random tags 229 | * @param tagCount The number of tags 230 | * @return the tag map 231 | */ 232 | public static Map randomTags(final int tagCount) { 233 | final Map tags = new LinkedHashMap(tagCount); 234 | for(int i = 0; i < tagCount; i++) { 235 | String[] frags = getRandomFragments(); 236 | tags.put(frags[0], frags[1]); 237 | } 238 | return tags; 239 | } 240 | 241 | 242 | 243 | 244 | 245 | /** 246 | * Nothing yet 247 | * @throws java.lang.Exception thrown on any error 248 | */ 249 | @BeforeClass 250 | public static void setUpBeforeClass() throws Exception { 251 | } 252 | 253 | 254 | /** 255 | * Nothing yet... 256 | * @throws java.lang.Exception thrown on any error 257 | */ 258 | @Before 259 | public void setUp() throws Exception { 260 | } 261 | 262 | 263 | 264 | 265 | /** 266 | * Out printer 267 | * @param fmt the message format 268 | * @param args the message values 269 | */ 270 | public static void log(Object fmt, Object...args) { 271 | OUT.println(String.format(fmt.toString(), args)); 272 | } 273 | 274 | // /** 275 | // * Returns the environment classloader for the passed TSDB config 276 | // * @param configName The config name 277 | // * @return The classloader that would be created for the passed config 278 | // */ 279 | // public static ClassLoader tsdbClassLoader(String configName) { 280 | // try { 281 | // Config config = getConfig(configName); 282 | // return TSDBPluginServiceLoader.getSupportClassLoader(config); 283 | // } catch (Exception ex) { 284 | // throw new RuntimeException("Failed to load config [" + configName + "]", ex); 285 | // 286 | // } 287 | // } 288 | 289 | 290 | 291 | /** 292 | * Err printer 293 | * @param fmt the message format 294 | * @param args the message values 295 | */ 296 | public static void loge(String fmt, Object...args) { 297 | ERR.print(String.format(fmt, args)); 298 | if(args!=null && args.length>0 && args[0] instanceof Throwable) { 299 | ERR.println(" Stack trace follows:"); 300 | ((Throwable)args[0]).printStackTrace(ERR); 301 | } else { 302 | ERR.println(""); 303 | } 304 | } 305 | 306 | /** A set of files to be deleted after each test */ 307 | protected static final Set TO_BE_DELETED = new CopyOnWriteArraySet(); 308 | 309 | 310 | 311 | 312 | /** 313 | * Generates an array of random strings created from splitting a randomly generated UUID. 314 | * @return an array of random strings 315 | */ 316 | public static String[] getRandomFragments() { 317 | return UUID.randomUUID().toString().split("-"); 318 | } 319 | 320 | /** 321 | * Generates a random string made up from a UUID. 322 | * @return a random string 323 | */ 324 | public static String getRandomFragment() { 325 | return UUID.randomUUID().toString(); 326 | } 327 | 328 | 329 | 330 | 331 | /** A serial number factory for stream threads */ 332 | public static final AtomicLong streamThreadSerial = new AtomicLong(); 333 | 334 | /** 335 | * Starts a stream thread 336 | * @param r The runnable to run 337 | * @param threadName the name of the thread 338 | */ 339 | public void startStream(Runnable r, String threadName) { 340 | Thread t = new Thread(r, threadName + "#" + streamThreadSerial.incrementAndGet()); 341 | t.setDaemon(true); 342 | t.start(); 343 | log("Started Thread [%s]", threadName); 344 | } 345 | 346 | public static Map getRandomTags() { 347 | final int size = nextPosInt(7)+1; 348 | final Map map = new HashMap(size); 349 | for(int i = 0; i < size; i++) { 350 | map.put(getRandomFragment(), getRandomFragment()); 351 | } 352 | return map; 353 | } 354 | 355 | 356 | 357 | } 358 | 359 | -------------------------------------------------------------------------------- /src/test/java/com/heliosapm/jvmti/Example.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2016 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package com.heliosapm.jvmti; 14 | 15 | import com.heliosapm.jvmti.agent.Agent; 16 | 17 | /** 18 | *

Title: Example

19 | *

Description: Simple examples of the agent

20 | *

Company: Helios Development Group LLC

21 | * @author Whitehead (nwhitehead AT heliosdev DOT org) 22 | *

com.heliosapm.jvmti.Example

23 | */ 24 | 25 | public class Example { 26 | 27 | /** 28 | * Some basic examples 29 | * @param args none. 30 | */ 31 | public static void main(String[] args) { 32 | 33 | final Agent agent = Agent.getInstance(); 34 | System.gc(); 35 | final long startTime = System.currentTimeMillis(); 36 | 37 | final int objectCount = agent.getInstanceCountOf(Object.class); 38 | log("Object instance count:%s", objectCount); 39 | 40 | final int charSequenceCount = agent.getInstanceCountOfAny(CharSequence.class); 41 | log("CharSequence instance count:%s", charSequenceCount); 42 | 43 | log("Elapsed:%s ms.", System.currentTimeMillis()-startTime); 44 | 45 | int total = 0; 46 | for(int i = 0; i < 100; i++) { 47 | final int oc = agent.getInstanceCountOf(Object.class); 48 | final int cc = agent.getInstanceCountOfAny(CharSequence.class); 49 | total += (oc + cc); 50 | System.gc(); 51 | } 52 | 53 | final long startTime2 = System.currentTimeMillis(); 54 | 55 | final int objectCount2 = agent.getInstanceCountOf(Object.class); 56 | log("Object instance count:%s", objectCount2); 57 | 58 | final int charSequenceCount2 = agent.getInstanceCountOfAny(CharSequence.class); 59 | log("CharSequence instance count:%s", charSequenceCount2); 60 | 61 | log("Elapsed:%s ms.", System.currentTimeMillis()-startTime2); 62 | 63 | 64 | } 65 | 66 | 67 | /** 68 | * Low maintenance formatted message logger 69 | * @param fmt The format 70 | * @param args The format args 71 | */ 72 | public static void log(final Object fmt, final Object...args) { 73 | System.out.println(String.format(fmt.toString(), args)); 74 | } 75 | 76 | 77 | } 78 | -------------------------------------------------------------------------------- /src/test/java/com/heliosapm/jvmti/InstancesExample.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2016 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package com.heliosapm.jvmti; 14 | 15 | import com.heliosapm.jvmti.agent.Agent; 16 | 17 | /** 18 | *

Title: InstancesExample

19 | *

Description: Example acquiring instances

20 | *

Company: Helios Development Group LLC

21 | * @author Whitehead (nwhitehead AT heliosdev DOT org) 22 | *

com.heliosapm.jvmti.InstancesExample

23 | */ 24 | 25 | public class InstancesExample { 26 | 27 | /** 28 | * Example acquiring instances 29 | * @param args none. 30 | */ 31 | public static void main(String[] args) { 32 | final Agent agent = Agent.getInstance(); 33 | System.gc(); 34 | String[] strings = agent.getInstancesOf(String.class, 20); 35 | for(int i = 10; i < 15; i++) { 36 | log("String #%s: [%s]", i, strings[i]); 37 | } 38 | strings = null; // Don't prevent gc of these objects ! 39 | 40 | CharSequence[] charSeqs = agent.getInstancesOfAny(CharSequence.class, 20); 41 | for(int i = 10; i < charSeqs.length-1; i++) { 42 | log("CharSequence#%s: Type:%s, Value:[%s]", i, charSeqs[i].getClass().getName(), charSeqs[i].toString()); 43 | } 44 | charSeqs = null; 45 | 46 | } 47 | 48 | /** 49 | * Low maintenance formatted message logger 50 | * @param fmt The format 51 | * @param args The format args 52 | */ 53 | public static void log(final Object fmt, final Object...args) { 54 | System.out.println(String.format(fmt.toString(), args)); 55 | } 56 | 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/test/java/com/heliosapm/jvmti/TopNExample.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2016 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package com.heliosapm.jvmti; 14 | 15 | import java.util.Map; 16 | 17 | import com.heliosapm.jvmti.agent.Agent; 18 | 19 | /** 20 | *

Title: TopNExample

21 | *

Description: Example for top n

22 | *

Company: Helios Development Group LLC

23 | * @author Whitehead (nwhitehead AT heliosdev DOT org) 24 | *

com.heliosapm.jvmti.TopNExample

25 | */ 26 | 27 | public class TopNExample { 28 | 29 | /** 30 | * Lists the top n 31 | * @param args None 32 | */ 33 | public static void main(String[] args) { 34 | final Agent agent = Agent.getInstance(); 35 | System.gc(); 36 | final Map, Long> topMap = agent.getTopNInstanceCounts(Object.class, 10, false); 37 | for(Map.Entry, Long> entry: topMap.entrySet()) { 38 | log("%s : %s", Agent.renderClassName(entry.getKey()), entry.getValue()); 39 | } 40 | topMap.clear(); // don't prevent gc ! 41 | } 42 | 43 | 44 | /** 45 | * Low maintenance formatted message logger 46 | * @param fmt The format 47 | * @param args The format args 48 | */ 49 | public static void log(final Object fmt, final Object...args) { 50 | System.out.println(String.format(fmt.toString(), args)); 51 | } 52 | 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/test/java/com/heliosapm/jvmti/options/TestAgentOptions.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2016 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package com.heliosapm.jvmti.options; 14 | 15 | import java.util.Map; 16 | import java.util.Properties; 17 | 18 | import org.junit.Test; 19 | import org.junit.Assert; 20 | 21 | import com.heliosapm.jvmti.install.AgentInstaller; 22 | import com.heliosapm.jvmti.install.AgentOption; 23 | 24 | /** 25 | *

Title: TestAgentOptions

26 | *

Description: Tests processing of packed agent options

27 | *

Company: Helios Development Group LLC

28 | * @author Whitehead (nwhitehead AT heliosdev DOT org) 29 | *

com.heliosapm.jvmti.options.TestAgentOptions

30 | */ 31 | 32 | public class TestAgentOptions { 33 | 34 | final Properties clProps; 35 | final String packedProps = "D:sna=fu" + AgentInstaller.DELIM_TERM + "D:foo=bar"; 36 | 37 | public TestAgentOptions() { 38 | clProps = new Properties(); 39 | clProps.setProperty("foo", "bar"); 40 | clProps.setProperty("sna", "fu"); 41 | } 42 | 43 | @Test 44 | public void testInstallSysProps() { 45 | final Map options = AgentOption.agentOptions(packedProps); 46 | final Properties p = (Properties)options.get(AgentOption.D); 47 | Assert.assertNotNull(p); 48 | Assert.assertEquals(2, p.size()); 49 | Assert.assertEquals("fu", p.getProperty("sna")); 50 | Assert.assertEquals("bar", p.getProperty("foo")); 51 | } 52 | 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/test/java/com/heliosapm/jvmti/options/TestCommandLine.java: -------------------------------------------------------------------------------- 1 | // This file is part of OpenTSDB. 2 | // Copyright (C) 2010-2016 The OpenTSDB Authors. 3 | // 4 | // This program is free software: you can redistribute it and/or modify it 5 | // under the terms of the GNU Lesser General Public License as published by 6 | // the Free Software Foundation, either version 2.1 of the License, or (at your 7 | // option) any later version. This program is distributed in the hope that it 8 | // will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 9 | // of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 | // General Public License for more details. You should have received a copy 11 | // of the GNU Lesser General Public License along with this program. If not, 12 | // see . 13 | package com.heliosapm.jvmti.options; 14 | 15 | import java.util.Map; 16 | import java.util.Properties; 17 | 18 | import org.junit.Assert; 19 | import org.junit.Test; 20 | 21 | import com.heliosapm.jvmti.BaseTest; 22 | import com.heliosapm.jvmti.install.AgentInstaller; 23 | import com.heliosapm.jvmti.install.AgentOption; 24 | 25 | /** 26 | *

Title: TestOptions

27 | *

Description: Tests for command line handling

28 | *

Company: Helios Development Group LLC

29 | * @author Whitehead (nwhitehead AT heliosdev DOT org) 30 | *

com.heliosapm.jvmti.options.TestOptions

31 | */ 32 | 33 | public class TestCommandLine extends BaseTest { 34 | 35 | final String PID = "2837"; 36 | final Properties clProps; 37 | final String packedProps = "D:sna=fu" + AgentInstaller.DELIM_TERM + "D:foo=bar"; 38 | 39 | public TestCommandLine() { 40 | clProps = new Properties(); 41 | clProps.setProperty("foo", "bar"); 42 | clProps.setProperty("sna", "fu"); 43 | } 44 | 45 | @Test 46 | public void testCommandLinePidOnlyOneArg() { 47 | testPidOnly(PID, "--pid=" + PID); 48 | } 49 | 50 | @Test 51 | public void testCommandLinePidOnlyTwoArgs() { 52 | testPidOnly(PID, "--pid", PID); 53 | } 54 | 55 | @Test 56 | public void testCommandLinePidOnlyUpperTwoArgs() { 57 | testPidOnly(PID, "--PID", PID); 58 | } 59 | 60 | @Test 61 | public void testBadCommandLinePidOnlyTwoArgs() { 62 | Assert.assertEquals("Empty value for option [PID]", testPidOnly(PID, "--pid=", PID)); 63 | } 64 | 65 | @Test 66 | public void testMissingPid() { 67 | Assert.assertEquals("Missing mandatory command line option[s]:[PID]", testPidOnly(PID)); 68 | } 69 | 70 | @Test 71 | public void testCommandLinePidTwoArgsPropsOneArg() { 72 | testPidAndProps(PID, "--d=sna=fu", "--PID", PID, "--d=foo=bar"); 73 | } 74 | 75 | @Test 76 | public void testCommandLinePidTwoArgsPropsTwoArgs() { 77 | testPidAndProps(PID, "--d", "sna=fu", "--PID", PID, "--d", "foo=bar"); 78 | } 79 | 80 | protected String testPidOnly(final String pid, final String...args) { 81 | final StringBuilder allAgentOptions = new StringBuilder(); 82 | final Map options; 83 | try { 84 | options = AgentOption.commandLine(allAgentOptions, args); 85 | } catch (Exception ex) { 86 | return ex.getMessage(); 87 | } 88 | Assert.assertEquals(pid, options.get(AgentOption.PID)); 89 | Assert.assertEquals(1, options.size()); 90 | Assert.assertEquals(0, allAgentOptions.length()); 91 | return null; 92 | } 93 | 94 | protected String testPidAndProps(final String pid, final String...args) { 95 | final StringBuilder allAgentOptions = new StringBuilder(); 96 | final Map options; 97 | try { 98 | options = AgentOption.commandLine(allAgentOptions, args); 99 | } catch (Exception ex) { 100 | return ex.getMessage(); 101 | } 102 | Assert.assertEquals(pid, options.get(AgentOption.PID)); 103 | Assert.assertEquals(1, options.size()); 104 | Assert.assertEquals(packedProps, allAgentOptions.toString()); 105 | return null; 106 | } 107 | 108 | 109 | 110 | 111 | } 112 | -------------------------------------------------------------------------------- /src/test/resources/config/config1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /src/test/resources/configs/config1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | --------------------------------------------------------------------------------