| /* |
| * 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; |
| } |
| } |