| /* | |
| * Licensed to the Apache Software Foundation (ASF) under one | |
| * or more contributor license agreements. See the NOTICE file | |
| * distributed with this work for additional information | |
| * regarding copyright ownership. The ASF licenses this file | |
| * to you under the Apache License, Version 2.0 (the | |
| * "License"); you may not use this file except in compliance | |
| * with the License. You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, | |
| * software distributed under the License is distributed on an | |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
| * KIND, either express or implied. See the License for the | |
| * specific language governing permissions and limitations | |
| * under the License. | |
| */ | |
| #include <jvmpi.h> | |
| #include <string.h> | |
| #include <stdlib.h> | |
| static JavaVM *jvm; | |
| static JNIEnv *env; | |
| static JVMPI_Interface *jvmpi; | |
| extern "C" JNIEXPORT jint JNICALL JVM_OnLoad(JavaVM *jvm, char *options, void *reserved); | |
| static void notifyEvent(JVMPI_Event *event); | |
| static int total = 0; | |
| const char * traced_methods[] = { | |
| // java.beans.PropertyChangeListener | |
| "propertyChange", "(Ljava/beans/PropertyChangeEvent;)V", | |
| // java.beans.VetoableChangeListener | |
| "vetoableChange", "(Ljava/beans/PropertyChangeEvent;)V", | |
| // javax.swing.event.ChangeListener | |
| "stateChanged", "(Ljavax/swing/event/ChangeEvent;)V", | |
| // javax.swing.event.DocumentListener | |
| "insertUpdate", "(Ljavax/swing/event/DocumentEvent;)V", | |
| "removeUpdate", "(Ljavax/swing/event/DocumentEvent;)V", | |
| "changedUpdate", "(Ljavax/swing/event/DocumentEvent;)V", | |
| // javax.swing.event.UndoableEditListener | |
| "undoableEditHappened", "(Ljavax/swing/event/UndoableEditEvent;)V", | |
| // org.netbeans.core.awt.BuButtonBar$ButtonBarListener | |
| "buttonPressed", "(L(org/netbeans/core/awt/ButtonBar$ButtonBarEvent;)V", | |
| // org.openide.util.datatransfer.ClipboardListener | |
| "clipboardChanged", "(Lorg/openide/util/datatransfer/ClipboardEvent;)V", | |
| // org.openide.compiler.CompilerListener | |
| "compilerProgress", "(Lorg.openide.compiler/ProgressEvent;)V", | |
| "compilerError", "(Lorg.openide.compiler.ErrorEvent;)V", | |
| // org.netbeans.core.execution.ExecutionListener | |
| "startedExecution", "(Lorg/netbeans/core/execution/ExecutionEvent;)V", | |
| "finishedExecution", "(Lorg/netbeans/core/execution/ExecutionEvent;)V", | |
| // org.openide.filesystems.FileChangeListener | |
| "fileFolderCreated", "(Lorg/openide/filesystems/FileEvent;)V", | |
| "fileDataCreated", "(Lorg/openide/filesystems/FileEvent;)V", | |
| "fileChanged", "(Lorg/openide/filesystems/FileEvent;)V", | |
| "fileDeleted", "(Lorg/openide/filesystems/FileEvent;)V", | |
| "fileRenamed", "(Lorg/openide/filesystems/FileRenameEvent;)V", | |
| "fileAttributeChanged", "(Lorg/openide/filesystems/FileAttributeEvent;)V" | |
| //org.netbeans.core.projects.FileStateManager$FileStatusListener | |
| "fileStatusChanged", "(Lorg/openide/filesystems/FileObject;)V", | |
| // org.openide.filesystems.FileStatusListener | |
| "annotationChanged", "(Lorg/openide/filesystems/FileStatusEvent;)V", | |
| // org.netbeans.core.windows.frames.FrameTypeListener | |
| "frameDeactivated", "(Lorg/netbeans/core/windows/frames/FrameTypeEvent;)V", | |
| "frameClosed", "(Lorg/netbeans/core/windows/frames/FrameTypeEvent;)V", | |
| "frameDeiconified", "(Lorg/netbeans/core/windows/frames/FrameTypeEvent;)V", | |
| "frameNormalized", "(Lorg/netbeans/core/windows/frames/FrameTypeEvent;)V", | |
| "frameOpened", "(Lorg/netbeans/core/windows/frames/FrameTypeEvent;)V", | |
| "frameIconified", "(Lorg/netbeans/core/windows/frames/FrameTypeEvent;)V", | |
| "frameClosing", "(Lorg/netbeans/core/windows/frames/FrameTypeEvent;)V", | |
| "frameActivated", "(Lorg/netbeans/core/windows/frames/FrameTypeEvent;)V", | |
| "frameMaximized", "(Lorg/netbeans/core/windows/frames/FrameTypeEvent;)V", | |
| // org.openide.util.LookupListener | |
| "resultChanged", "(Lorg/openide/util/LookupEvent;)V", | |
| // org.openide.nodes.NodeListener | |
| "childrenAdded", "(Lorg/openide/nodes/NodeMemberEvent;)V", | |
| "childrenRemoved", "(Lorg/openide/nodes/NodeMemberEvent;)V", | |
| "childrenReordered", "(Lorg/openide/nodes/NodeReorderEvent;)V", | |
| "nodeDestroyed", "(Lorg/openide/nodes/NodeEvent;)V", | |
| // org.openide.loaders.OperationListener | |
| "operationPostCreate", "(Lorg/openide/loaders/OperationEvent;)V", | |
| "operationCopy", "(Lorg/openide/loaders/OperationEvent$Copy;)V", | |
| "operationMove", "(Lorg/openide/loaders/OperationEvent$Move;)V", | |
| "operationDelete", "(Lorg/openide/loaders/OperationEvent;)V", | |
| "operationRename", "(Lorg/openide/loaders/OperationEvent$Rename;)V", | |
| "operationCreateShadow", "(Lorg/openide/loaders/OperationEvent$Copy;)V", | |
| "operationCreateFromTemplate", "(Lorg/openide/loaders/OperationEvent$Copy;)V", | |
| // org.openide.filesystems.RepositoryListener | |
| "fileSystemAdded", "(Lorg/openide/filesystems/RepositoryEvent;)V", | |
| "fileSystemRemoved", "(Lorg/openide/filesystems/RepositoryEvent;)V", | |
| "fileSystemPoolReordered", "(Lorg/openide/filesystems/RepositoryReorderedEvent;)V", | |
| // org.openide.util.TaskListener | |
| "taskFinished", "(Lorg/openide/util/Task;)V", | |
| // org.netbeans.core.windows.TopComponentListener | |
| "topComponentActivated", "(Lorg/netbeans/core/windows/TopComponentChangedEvent;)V", | |
| "topComponentOpened", "(Lorg/netbeans/core/windows/TopComponentChangedEvent;)V", | |
| "topComponentClosed", "(Lorg/netbeans/core/windows/TopComponentChangedEvent;)V", | |
| "selectedNodesChanged", "(Lorg/netbeans/core/windows/SelectedNodesChangedEvent;)V", | |
| // org.openide.util.datatransfer.TransferListener | |
| "accepted", "(I)V", | |
| "rejected", "(V)V" | |
| "ownershipLost", "(V)V", | |
| NULL }; | |
| typedef struct { | |
| jmethodID method_id; | |
| const char *class_name; | |
| const char *method_name; | |
| const char *method_sig; | |
| int counter; | |
| } mentry_t; | |
| static mentry_t* allMethods[1024 * 16]; | |
| static int allMethodsLength = 0; | |
| mentry_t* lookupMethodIDHelper(jmethodID mid, int start, int end) { | |
| if (start >= end) | |
| return NULL; | |
| int pivot = (start + end) / 2; | |
| if (mid == allMethods[pivot]->method_id) | |
| return allMethods[pivot]; | |
| mentry_t* e = lookupMethodIDHelper(mid, start, pivot - 1); | |
| if (e != NULL) | |
| return e; | |
| else | |
| return lookupMethodIDHelper(mid, pivot + 1, end); | |
| } | |
| mentry_t* lookupMethodID(jmethodID mid) { | |
| return lookupMethodIDHelper(mid, 0, allMethodsLength); | |
| } | |
| void storeNewMethodID(jmethodID mid, const char* classname, const char* mname, const char* msig) { | |
| if (allMethodsLength >= (sizeof allMethods / sizeof allMethods[0])) { | |
| fprintf(stderr, "jtrace> allMethods buffer exceeded\n"); | |
| exit(1); | |
| } | |
| mentry_t* e = new mentry_t; | |
| e->method_id = mid; | |
| e->class_name = strdup(classname); | |
| e->method_name = strdup(mname); | |
| e->method_sig = strdup(msig); | |
| e->counter = 0; | |
| int i = 0; | |
| while (i < allMethodsLength && allMethods[i]->method_id < mid) { | |
| i++; | |
| } | |
| int j = allMethodsLength; | |
| while (j > i) { | |
| allMethods[j] = allMethods[j-1]; | |
| j--; | |
| } | |
| allMethods[i] = e; | |
| allMethodsLength++; | |
| } | |
| JNIEXPORT jint JNICALL JVM_OnLoad(JavaVM *aJvm, char *options, void *reserved) { | |
| fprintf(stderr, "jtrace> %s\n", "initializing ....."); | |
| jvm = aJvm; | |
| if (jvm->GetEnv((void**)&env, JNI_VERSION_1_2)) { | |
| fprintf(stderr, "jtrace> %s\n", "error in obtaining JNI interface pointer"); | |
| return JNI_ERR; | |
| } | |
| if ((jvm->GetEnv((void **)&jvmpi, JVMPI_VERSION_1)) < 0) { | |
| fprintf(stderr, "jtrace> %s\n", "error in obtaining JVMPI interface pointer"); | |
| return JNI_ERR; | |
| } | |
| jvmpi->NotifyEvent = notifyEvent; | |
| jvmpi->EnableEvent(JVMPI_EVENT_CLASS_LOAD, NULL); | |
| jvmpi->EnableEvent(JVMPI_EVENT_METHOD_ENTRY2, NULL); | |
| jvmpi->EnableEvent(JVMPI_EVENT_METHOD_EXIT, NULL); | |
| fprintf(stderr, "jtrace> %s\n", ".... ok\n"); | |
| return JNI_OK; | |
| } | |
| void notifyEvent(JVMPI_Event *event) { | |
| switch (event->event_type) { | |
| case JVMPI_EVENT_CLASS_LOAD: | |
| { | |
| // fprintf(stderr, "jtrace> loaded %s\n", event->u.class_load.class_name); | |
| for (int i = 0; i < event->u.class_load.num_methods; i++) { | |
| JVMPI_Method *m = & event->u.class_load.methods[i]; | |
| // fprintf(stderr, "jtrace> %s%s\n", m->method_name, m->method_signature); | |
| for (const char **p = traced_methods; *p != NULL; p += 2) { | |
| if (0 == strcmp(m->method_name, *p) && 0 == strcmp(m->method_signature, *(p+1))) { | |
| storeNewMethodID(m->method_id, event->u.class_load.class_name, | |
| m->method_name, m->method_signature); | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| break; | |
| case JVMPI_EVENT_METHOD_ENTRY2: | |
| { | |
| jmethodID mid = event->u.method_entry2.method_id; | |
| jobjectID obj = event->u.method_entry2.obj_id; | |
| mentry_t* e = lookupMethodID(mid); | |
| // if (e == NULL) { | |
| // jobjectID classid = jvmpi->GetMethodClass(mid); | |
| // jint res = jvmpi->RequestEvent(JVMPI_EVENT_CLASS_LOAD, classid); | |
| // if (res != JVMPI_SUCCESS) { | |
| // // warning | |
| // } | |
| // e = lookupMethodID(mid); | |
| // } | |
| if (e != NULL) { | |
| fprintf(stderr, "jtrace> [%5d,%5d] <<< ENTERED %s.%s%s\n", (total++), (e->counter++), e->class_name, e->method_name, e->method_sig); | |
| } | |
| } | |
| break; | |
| case JVMPI_EVENT_METHOD_EXIT: | |
| { | |
| jmethodID mid = event->u.method.method_id; | |
| mentry_t* e = lookupMethodID(mid); | |
| if (e != NULL) { | |
| fprintf(stderr, "jtrace> [%5d,%5d] >>> EXITED %s.%s%s\n", (total), (e->counter), e->class_name, e->method_name, e->method_sig); | |
| } | |
| } | |
| break; | |
| } | |
| } |