diff options
Diffstat (limited to 'java')
-rw-r--r-- | java/Hello.java | 21 | ||||
-rw-r--r-- | java/Makefile.am | 46 | ||||
-rw-r--r-- | java/README | 40 | ||||
-rw-r--r-- | java/TestRtflObjects1.java | 50 | ||||
-rw-r--r-- | java/class.c | 39 | ||||
-rw-r--r-- | java/class.h | 9 | ||||
-rw-r--r-- | java/field.c | 156 | ||||
-rw-r--r-- | java/field.h | 12 | ||||
-rw-r--r-- | java/main.c | 68 | ||||
-rw-r--r-- | java/method.c | 111 | ||||
-rw-r--r-- | java/method.h | 12 | ||||
-rw-r--r-- | java/misc.c | 191 | ||||
-rw-r--r-- | java/misc.h | 41 |
13 files changed, 796 insertions, 0 deletions
diff --git a/java/Hello.java b/java/Hello.java new file mode 100644 index 0000000..04eadd7 --- /dev/null +++ b/java/Hello.java @@ -0,0 +1,21 @@ +package rtfl; + +public class Hello +{ + int i1; + Integer i2; + String s1; + + public static void main (String[] args) + { + new Hello().hello(); + } + + public void hello() + { + System.out.println ("===== Hello Java! ====="); + i1 = 5; + i2 = new Integer(7); + s1 = "Hi!"; + } +} diff --git a/java/Makefile.am b/java/Makefile.am new file mode 100644 index 0000000..9d3f511 --- /dev/null +++ b/java/Makefile.am @@ -0,0 +1,46 @@ +AM_CFLAGS = -Wall $(JAVA_CFLAGS) + +JAVA = $(JAVA_HOME)/jre/bin/java +JAVAC = $(JAVA_HOME)/bin/javac + +lib_LTLIBRARIES = librtfl-jvm-ti.la + +librtfl_jvm_ti_la_SOURCES = \ + main.c \ + class.h \ + class.c \ + method.h \ + method.c \ + field.h \ + field.c \ + config.h \ + config.c \ + misc.h \ + misc.c + +EXTRA_DIST = README Hello.java TestRtflObjects1.java + +# Run tests without installation. +LIBPATH=./.libs + +run-hello: $(LIBPATH)/librtfl-jvm-ti.so rtfl/Hello.class + LD_LIBRARY_PATH=$(LIBPATH) $(JAVA) -agentlib:rtfl-jvm-ti rtfl.Hello + +run-test-rtfl-objects-1: $(LIBPATH)/librtfl-jvm-ti.so rtfl/TestRtflObjects1.class + LD_LIBRARY_PATH=$(LIBPATH) $(JAVA) -agentlib:rtfl-jvm-ti rtfl.TestRtflObjects1 + +run-test-rtfl-objects-2: $(LIBPATH)/librtfl-jvm-ti.so rtfl/TestRtflObjects2.class + LD_LIBRARY_PATH=$(LIBPATH) $(JAVA) -agentlib:rtfl-jvm-ti rtfl.TestRtflObjects2 + +rtfl/Hello.class: Hello.java + $(JAVAC) -g -d . Hello.java + +rtfl/TestRtflObjects1.class: TestRtflObjects1.java + $(JAVAC) -g -d . TestRtflObjects1.java + +rtfl/TestRtflObjects2.class: TestRtflObjects2.java + $(JAVAC) -g -d . TestRtflObjects2.java + +clean-local: + find -name "*.class" | xargs rm -f + diff --git a/java/README b/java/README new file mode 100644 index 0000000..e0196a0 --- /dev/null +++ b/java/README @@ -0,0 +1,40 @@ +This sub-project aims to provide an agent for the Java Virtual +Machine, which uses the JVM Tool Interface (see [1] and [2]) to print +RTFL messages for Java programs, which must not necessarily be +prepared. This makes using RTFL much less time-consuming and so lowers +the entrance barrier. + +The agent consists of the file "librtfl-jvm-ti.so", which is passed to +"java" by the option "-agentlib:rtfl-jvm-ti"; see Makefile.am for +details. + +Run "make run-hello" or "run run-test-rtfl-objects-1" to run some +sample programs with the agent. + + +How it works +------------ +Several commands from the object module (like "create") follow the +program structure, and can so easily be printed when the respective +JVM-TI event is processed. Others (like "enter") can be printed by +this approach, but some parameters are missing (aspect and priority) +and so replaced by standard values (empty aspect and priority 0). It +is planned to make these parameters configurable, either by files or +by annotations. + +A third group of commands (like "msg") must still be added explicitly +to code. (For "msg", one could think of an integration with existing +logging frameworks.) + +Since Java programs often use many third-party code (libraries, +containers, ...), it is crucial for the performance to filter quite +early, and so reduce the total amount of messages. Filtering by +packages seems feasible (like "com.acme" and sub-packages when code of +ACME is debugged). Currently, only "rtfl" and its sub-packages are +considered; this is hard-coded in the function include_class() in +"config.c". + +---------------------------------------------------------------------- + +[1] http://docs.oracle.com/javase/8/docs/technotes/guides/jvmti/index.html +[2] http://www.oracle.com/technetwork/articles/javase/jvmti-136367.html diff --git a/java/TestRtflObjects1.java b/java/TestRtflObjects1.java new file mode 100644 index 0000000..fece128 --- /dev/null +++ b/java/TestRtflObjects1.java @@ -0,0 +1,50 @@ +package rtfl; + +import java.util.LinkedList; +import java.util.List; + +public class TestRtflObjects1 +{ + private static class A + { + private A other = null; + private List<Integer> numbers = new LinkedList<Integer>(); + + public void setOther (A other) + { + this.other = other; + } + + public int doSomething (int n) + { + int r = (int)(Math.random() * 251); + numbers.add (new Integer (r)); + + if (other != null && n > 0) + other.doSomething (n - 1); + + return r; + } + } + + private static class B extends A + { + } + + private static class C extends A + { + } + + public static void main (String[] args) + { + A x = new A (); + B y = new B (); + C z = new C (); + + x.setOther (y); + y.setOther (z); + z.setOther (x); + + x.doSomething (8); + } +} diff --git a/java/class.c b/java/class.c new file mode 100644 index 0000000..d4e78ae --- /dev/null +++ b/java/class.c @@ -0,0 +1,39 @@ +#include "class.h" +#include "config.h" +#include "misc.h" + +void JNICALL class_prepare(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, + jclass klass) +{ + jvmtiError error; + char *class_sig = NULL, *class_name = NULL; + + if ((error = (*jvmti)->GetClassSignature (jvmti, klass, &class_sig, NULL)) + != JVMTI_ERROR_NONE) + jvmti_error (jvmti, error, "GetClassSignature"); + else { + if ((class_name = get_class_name_from_sig (class_sig, TRUE)) && + include_class (class_name)) { + jint field_count; + jfieldID* fields; + if ((error = (*jvmti)->GetClassFields (jvmti, klass, &field_count, + &fields)) + != JVMTI_ERROR_NONE) + jvmti_error (jvmti, error, "GetClassFields"); + else { + int i; + for (i = 0; i < field_count; i++) { + if ((error = + (*jvmti)->SetFieldModificationWatch (jvmti, klass, + fields[i])) + != JVMTI_ERROR_NONE) + jvmti_error (jvmti, error, "SetFieldModificationWatch"); + } + } + } + } + + jvmti_dealloc (jvmti, class_sig); + simple_free (class_name); + +} diff --git a/java/class.h b/java/class.h new file mode 100644 index 0000000..41ffb74 --- /dev/null +++ b/java/class.h @@ -0,0 +1,9 @@ +#ifndef __JAVA_CLASS_H__ +#define __JAVA_CLASS_H__ + +#include <jvmti.h> + +void JNICALL class_prepare(jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, + jclass klass); + +#endif /* __JAVA_CLASS_H__ */ diff --git a/java/field.c b/java/field.c new file mode 100644 index 0000000..3427218 --- /dev/null +++ b/java/field.c @@ -0,0 +1,156 @@ +#include <string.h> +#include <stdlib.h> + +#include "field.h" +#include "config.h" +#include "misc.h" + +/* ---------------------------------------------------------------------- + + A sketch on how field modifications could be processed: + + A field modification results in a "set" or in an "assoc" + command. Neither is the field name *necessarily* the name of the "set" + command (or "assoc", if it would support names), nor is the field + value necessarily the respective value. Instead, the following rules + are used to determine the structure of the fields. + + (A) If the value type is "printable" (i. e. primitive, or a wrapper + class for a primitive type, or java.lang.String, or an enum (more + cases?)), then a set command is printed, with the name and the value + of the field. + + (B) If the value is a visible object, and it is not configured as + sub-structure (see below), an "assoc" command is printed, between this + object and the value object. (As soon as associations get a name, the + field name is used for this.) + + (C) If the value is an invisible object, or a visible object which is + configured as sub-structure, regard it as structured, according to the + rules which follow. + + Structured values are divided into sub-values with sub-names; the + field name, followed by a dot, is used as base-name; all sub-values + and sub-names are processed recursively according to the same rules, + while the base-name is eventually prepended to all sub-names. + + (C1) If the value is of type java.util.Map, and the keys are printable + (see (A)), use the keys as sub-names, the values as sub-values. ("The + keys are printable": does this mean that the type for the keys is a + class which is generally printable?) + + (C2) If the value is of type java.util.Map, and the keys are not + printable (see also C1), regard the value as unsorted list of + elements, which are themselves structured sub-values + (java.util.Map.Entry?). + + (C3) If the value is of type java.util.List, use the indices as + sub-names, and the elements as sub-values. + + (C4) If the value is an unsorted collection, i. e. of type + java.util.Collection, but not of java.util.Map or java.util.List, + apply a random order and proceed as in (C4). (Unsorted collections are + not really supported in RTFL.) + + The rules (C1) to (C4) apply not when configured otherwise ("do not + show logical structure"). + + (C5) Otherwise (not of type java.util.Collection or java.util.Map), + examine fields of the value; regard the names of its fields as + sub-names; their values as sub-values; + + (Alternative: Java Beans?) + + For simplification, ignore C1 to C4 in the first place and focus on + C5; then implement C1 to C4 by and by. + + ---------------------------------------------------------------------- */ + +static void print_field (jvmtiEnv *jvmti, JNIEnv* jni, jclass field_klass, + jobject object, jfieldID field, jvalue value, + char *base_name); +static void print_string_field (jvmtiEnv *jvmti, JNIEnv* jni, char *object_str, + char *base_name, char *field_name, + jstring string); + +void JNICALL field_modification (jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, + jmethodID method, jlocation location, + jclass field_klass, jobject object, + jfieldID field, char signature_type, + jvalue new_value) +{ + // The class name is irrelevant here, since classes are already + // filtered inclass_prepare(), where SetFieldModificationWatch() + // is called. + + print_field (jvmti, jni, field_klass, object, field, new_value, ""); +} + +void print_field (jvmtiEnv *jvmti, JNIEnv* jni, jclass field_klass, + jobject object, jfieldID field, jvalue value, char *base_name) +{ + jvmtiError error; + char object_buf1[SIZE_OBJECT_BUF], object_buf2[SIZE_OBJECT_BUF]; + char *class_name = NULL, *field_name = NULL, *field_sig = NULL; + + if ((error = (*jvmti)->GetFieldName (jvmti, field_klass, field, &field_name, + &field_sig, NULL)) + != JVMTI_ERROR_NONE) + jvmti_error (jvmti, error, "GetFieldName"); + else { + //printf ("==> %s - %s\n", field_name, field_sig); + + fill_object_buf (jni, object_buf1, object); + + if ((class_name = get_class_name_from_sig (field_sig, FALSE))) { + if (include_class (class_name)) { + // The field represents an instance of a class which is also + // included: this is an association, not a simple field. + // ("field_name" could be used here, as soon as associations get + // a name.) + + if (value.l) { + fill_object_buf (jni, object_buf2, value.l); + RTFL_OBJ_PRINT ("assoc", "s:s", object_buf1, object_buf2); + } + } else { + if (strcmp (class_name, "java.lang.String")) { + print_string_field (jvmti, jni, object_buf1, base_name, + field_name, (jstring)(value.l)); + + } + } + } + } + + jvmti_dealloc (jvmti, field_name); + jvmti_dealloc (jvmti, field_sig); + simple_free (class_name); +} + +void print_string_field (jvmtiEnv *jvmti, JNIEnv* jni, char *object_str, + char *base_name, char *field_name, jstring string) +{ + if (string == NULL) + RTFL_OBJ_PRINT ("set", "s:ss:s", object_str, base_name, field_name, + "null"); + else { + // TODO: JNI calls somehow cause an abortion. Just some test + // code. + + //jsize len = (*jni)->GetStringLength (jni, string); + //const jchar *chars = (*jni)->GetStringChars (jni, string, NULL); + + //char *chars0 = (char*)malloc (len + 1); + //memcpy (chars0, chars, len); + //chars0[len] = 0; + + char *chars0 = "?\"?\"?"; + + RTFL_OBJ_PRINT ("set", "s:ss:\"q\"", object_str, base_name, field_name, + chars0); + + //free (chars0); + //(*jni)->ReleaseStringChars (jni, string, chars); + } +} diff --git a/java/field.h b/java/field.h new file mode 100644 index 0000000..6d580ee --- /dev/null +++ b/java/field.h @@ -0,0 +1,12 @@ +#ifndef __JAVA_FIELD_H__ +#define __JAVA_FIELD_H__ + +#include <jvmti.h> + +void JNICALL field_modification (jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, + jmethodID method, jlocation location, + jclass field_klass, jobject object, + jfieldID field, char signature_type, + jvalue new_value); + +#endif /* __JAVA_FIELD_H__ */ diff --git a/java/main.c b/java/main.c new file mode 100644 index 0000000..dc7e954 --- /dev/null +++ b/java/main.c @@ -0,0 +1,68 @@ +#include <jvmti.h> +#include <string.h> +#include <stdlib.h> + +#include "class.h" +#include "method.h" +#include "field.h" +#include "misc.h" + +JNIEXPORT jint JNICALL Agent_OnLoad (JavaVM *jvm, char *options, void *reserved) +{ + rtfl_print ("obj", RTFL_OBJ_VERSION, "", 0, "s", "noident"); + + jvmtiEnv *jvmti = NULL; + + (*jvm)->GetEnv (jvm, (void**)&jvmti, JVMTI_VERSION_1_0); + + jvmtiCapabilities capa; + memset (&capa, 0, sizeof(jvmtiCapabilities)); + capa.can_generate_method_entry_events = 1; + capa.can_generate_method_exit_events = 1; + capa.can_access_local_variables = 1; + capa.can_generate_field_modification_events = 1; + + jvmtiEventCallbacks callbacks; + (void)memset(&callbacks, 0, sizeof(callbacks)); + callbacks.ClassPrepare = &class_prepare; + callbacks.MethodEntry = &method_entry; + callbacks.MethodExit = &method_exit; + callbacks.FieldModification = &field_modification; + + jvmtiError error; + if ((error = (*jvmti)->AddCapabilities(jvmti, &capa)) != JVMTI_ERROR_NONE) + jvmti_error (jvmti, error, "AddCapabilities"); + else if ((error = + (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, + JVMTI_EVENT_CLASS_PREPARE, + (jthread)NULL)) + != JVMTI_ERROR_NONE) + jvmti_error (jvmti, error, + "SetEventNotificationMode (JVMTI_EVENT_CLASS_PREPARE)"); + else if ((error = + (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, + JVMTI_EVENT_METHOD_ENTRY, + (jthread)NULL)) + != JVMTI_ERROR_NONE) + jvmti_error (jvmti, error, + "SetEventNotificationMode (JVMTI_EVENT_METHOD_ENTRY)"); + else if ((error = + (*jvmti)->SetEventNotificationMode (jvmti, JVMTI_ENABLE, + JVMTI_EVENT_METHOD_EXIT, + (jthread)NULL)) + != JVMTI_ERROR_NONE) + jvmti_error (jvmti, error, + "SetEventNotificationMode (JVMTI_EVENT_METHOD_EXIT)"); + else if ((error = + (*jvmti)->SetEventNotificationMode + (jvmti, JVMTI_ENABLE, JVMTI_EVENT_FIELD_MODIFICATION, + (jthread)NULL)) != JVMTI_ERROR_NONE) + jvmti_error (jvmti, error, + "SetEventNotificationMode (JVMTI_EVENT_FIELD_MODIFICATION"); + else if ((error = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, + (jint)sizeof(callbacks))) + != JVMTI_ERROR_NONE) + jvmti_error (jvmti, error, "SetEventCallbacks"); + + return JNI_OK; +} diff --git a/java/method.c b/java/method.c new file mode 100644 index 0000000..35e6022 --- /dev/null +++ b/java/method.c @@ -0,0 +1,111 @@ +#include <string.h> + +#include "method.h" +#include "config.h" +#include "misc.h" + +static bool handle_method (jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, + jmethodID method, char **object, char **method_name, + char **class_name) +{ + jvmtiError error; + char *class_sig = NULL, object_buf[SIZE_OBJECT_BUF]; + jclass klass; + bool success = FALSE; + + *object = *method_name = *class_name = NULL; + + if ((error = + (*jvmti)->GetMethodName (jvmti, method, method_name, NULL, NULL)) + != JVMTI_ERROR_NONE) + jvmti_error (jvmti, error, "GetMethodName"); + else if ((error = + (*jvmti)->GetMethodDeclaringClass (jvmti, method, &klass)) + != JVMTI_ERROR_NONE) + jvmti_error (jvmti, error, "GetMethodDeclaringClass"); + else if ((error = + (*jvmti)->GetClassSignature (jvmti, klass, &class_sig, NULL)) + != JVMTI_ERROR_NONE) + jvmti_error (jvmti, error, "GetClassSignature"); + else { + if ((*class_name = get_class_name_from_sig (class_sig, TRUE)) && + include_class (*class_name)) { + jint numlocals; + jvmtiLocalVariableEntry *locals; + if ((error = (*jvmti)->GetLocalVariableTable (jvmti, method, + &numlocals, &locals)) + != JVMTI_ERROR_NONE) + jvmti_error (jvmti, error, "GetLocalVariableTable"); + else { + jint j, this_slot = -1; + for (j = 0; j < numlocals && this_slot == -1; j++) + if (strcmp (locals[j].name, "this") == 0) + this_slot = locals[j].slot; + + jobject this_obj; + + if (this_slot == -1) { + this_obj = NULL; + success = TRUE; + } else { + if ((error = (*jvmti)->GetLocalObject (jvmti, thread, 0, + this_slot, &this_obj)) + != JVMTI_ERROR_NONE) { + jvmti_error (jvmti, error, "GetLocalObject"); + } else + success = TRUE; + } + + if (success) { + fill_object_buf (jni, object_buf, this_obj); + *object = strdup (object_buf); + } + } + } + } + + jvmti_dealloc (jvmti, class_sig); + + return success; +} + +static void handle_method_free (jvmtiEnv *jvmti, char *object, + char *method_name, char *class_name) +{ + simple_free (object); + jvmti_dealloc (jvmti, method_name); + simple_free (class_name); +} + +void JNICALL method_entry (jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, + jmethodID method) +{ + char *object, *method_name, *class_name; + + if (handle_method (jvmti, jni, thread, method, &object, &method_name, + &class_name)) { + if (strcmp (method_name, "<init>") == 0) + RTFL_OBJ_PRINT ("create", "s:s", object, class_name); + else + RTFL_OBJ_PRINT ("enter", "s:s:d:s:", + object, "", 0, method_name, class_name); + } + + handle_method_free (jvmti, object, method_name, class_name); +} + +void JNICALL method_exit (jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, + jmethodID method, jboolean was_popped_by_exception, + jvalue return_value) +{ + char *object, *method_name, *class_name; + + if (handle_method (jvmti, jni, thread, method, &object, &method_name, + &class_name)) { + if (strcmp (method_name, "<init>") != 0) + // Hopefully, this is the correct method. + RTFL_OBJ_PRINT ("leave", "s", object); + } + + handle_method_free (jvmti, object, method_name, class_name); +} diff --git a/java/method.h b/java/method.h new file mode 100644 index 0000000..e3dfc45 --- /dev/null +++ b/java/method.h @@ -0,0 +1,12 @@ +#ifndef __JAVA_METHOD_H__ +#define __JAVA_METHOD_H__ + +#include <jvmti.h> + +void JNICALL method_entry (jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, + jmethodID method); +void JNICALL method_exit (jvmtiEnv *jvmti, JNIEnv* jni, jthread thread, + jmethodID method, jboolean was_popped_by_exception, + jvalue return_value); + +#endif /* __JAVA_METHOD_H__ */ diff --git a/java/misc.c b/java/misc.c new file mode 100644 index 0000000..09d0668 --- /dev/null +++ b/java/misc.c @@ -0,0 +1,191 @@ +#include <string.h> +#include <stdlib.h> + +#include "misc.h" + +bool str_starts_with (const char *haystack, const char *needle) +{ + int haystack_len = strlen (haystack), needle_len = strlen (needle); + if (haystack_len < needle_len) + return FALSE; + else + return memcmp (haystack, needle, needle_len * sizeof (char)) == 0; +} + +void jvmti_error (jvmtiEnv *jvmti, jvmtiError error, const char *fmt, ...) +{ + char *errorname; + if ((*jvmti)->GetErrorName (jvmti, error, &errorname) != JVMTI_ERROR_NONE) + errorname = "<error getting error name>"; + + fprintf(stderr, "error (jvmtiError %d: %s): ", error, errorname); + + va_list argp; + va_start(argp, fmt); + vfprintf(stderr, fmt, argp); + va_end(argp); + + fprintf(stderr, "\n"); +} + +void other_error (const char *fmt, ...) +{ + fprintf(stderr, "error (other): "); + + va_list argp; + va_start(argp, fmt); + vfprintf(stderr, fmt, argp); + va_end(argp); + + fprintf(stderr, "\n"); +} + +void jvmti_dealloc (jvmtiEnv *jvmti, void *mem) +{ + if (mem) { + jvmtiError error; + if ((error = (*jvmti)->Deallocate (jvmti, mem)) != JVMTI_ERROR_NONE) + jvmti_error (jvmti, error, "Deallocate"); + } +} + +void simple_free (void *mem) +{ + if (mem) + free (mem); +} + +char *get_class_name_from_sig (const char *class_sig, bool expect_class) +{ + int len_class_sig = strlen (class_sig); + if (!(class_sig[0] == 'L' && class_sig[len_class_sig - 1] == ';')) { + if (expect_class) + other_error ("don't know how to deal with class signature '%s'", + class_sig); + return NULL; + } else { + char *class_name = + (char*)malloc ((len_class_sig - 2 + 1) * sizeof (char)); + int i; + for (i = 0; i < len_class_sig - 2; i++) + class_name[i] = class_sig[i + 1] == '/' ? '.' : class_sig[i + 1]; + class_name[len_class_sig - 2] = 0; + return class_name; + } +} + +/* ------------------------------------------------------------------- + + The only way to identify objects seems to be the JNI method + IsSameObject; although jobject is a pointer, the equality of two + jobject's does not imply the identity of the represented Java + objects. + + Furthermore, since we only have one object at hand to get an + identifier needed for RTFL messages, we store all objects in a + list, after creating a global reference (NewGlobalRef); the index + in the list then identifies the object. This foils the garbage + collection, but for short debugging sessions, this should be + acceptable. + + ---------------------------------------------------------------------- */ + +static size_t reg_objects_size = 0, reg_objects_alloc_size; +static jobject *reg_objects = NULL; + +size_t object_index (JNIEnv* jni, jobject object) +{ + size_t i; + for (i = 0; i < reg_objects_size; i++) + if ((*jni)->IsSameObject (jni, object, reg_objects[i])) + return i; + + reg_objects_size++; + if (reg_objects == NULL) { + reg_objects_alloc_size = 1; + reg_objects = + (jobject*)malloc (reg_objects_alloc_size * sizeof (jobject)); + } else { + reg_objects_alloc_size <<= 1; + reg_objects = + (jobject*)realloc (reg_objects, + reg_objects_alloc_size * sizeof (jobject)); + } + + i = reg_objects_size - 1; + reg_objects[i] = (*jni)->NewGlobalRef (jni, object); + return i; +} + +void fill_object_buf (JNIEnv* jni, char *object_buf, jobject object) +{ + if (object) + snprintf (object_buf, SIZE_OBJECT_BUF, "%ld", object_index (jni, object)); + else + strcpy (object_buf, "null"); +} + +// Copied from "debug_rtfl.hh". +void rtfl_print (const char *module, const char *version, + const char *file, int line, const char *fmt, ...) +{ + // "\n" at the beginning just in case that the previous line is not + // finished yet. + printf ("\n[rtfl-%s-%s]%s:%d:pid(todo):", module, version, file, line); + + va_list args; + va_start (args, fmt); + + int i; + for (i = 0; fmt[i]; i++) { + int n, j; + void *p; + char *s; + + switch (fmt[i]) { + case 'd': + n = va_arg(args, int); + printf ("%d", n); + break; + + case 'p': + p = va_arg(args, void*); + printf ("%p", p); + break; + + case 's': + s = va_arg (args, char*); + for (j = 0; s[j]; j++) { + if (s[j] == ':' || s[j] == '\\') + putchar ('\\'); + putchar (s[j]); + } + break; + + case 'q': + s = va_arg (args, char*); + for (j = 0; s[j]; j++) { + if (s[j] == ':' || s[j] == '\\') + putchar ('\\'); + else if (s[j] == '\"') + printf ("\\\\"); // a quoted quoting character + putchar (s[j]); + } + break; + + case 'c': + n = va_arg(args, int); + printf ("#%06x", n); + break; + + default: + putchar (fmt[i]); + break; + } + } + + va_end (args); + + putchar ('\n'); + fflush (stdout); +} diff --git a/java/misc.h b/java/misc.h new file mode 100644 index 0000000..e88879e --- /dev/null +++ b/java/misc.h @@ -0,0 +1,41 @@ +#ifndef __JAVA_MISC_H__ +#define __JAVA_MISC_H__ + +#include <jvmti.h> + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +#ifndef __GNUC__ +# define __attribute__(x) /* nothing */ +#endif + +typedef enum { FALSE = 0, TRUE = 1 } bool; +enum { SIZE_OBJECT_BUF = 32 }; + +bool str_starts_with (const char *haystack, const char *needle); + +void jvmti_error (jvmtiEnv *jvmti, jvmtiError error, const char *fmt, ...) + __attribute__((format(printf, 3, 4))); +void other_error (const char *fmt, ...) __attribute__((format(printf, 1, 2))); + +void jvmti_dealloc (jvmtiEnv *jvmti, void *mem); +void simple_free (void *mem); + +char *get_class_name_from_sig (const char *class_sig, bool expect_class); + +size_t object_index (JNIEnv* jni, jobject object); +void fill_object_buf (JNIEnv* jni, char *object_buf, jobject object); + +void rtfl_print (const char *module, const char *version, + const char *file, int line, const char *fmt, ...); + +#define RTFL_PRINT(module, version, cmd, fmt, ...) \ + rtfl_print (module, version, "", 1, "s:" fmt, cmd, __VA_ARGS__) + +#define RTFL_OBJ_VERSION "1.0" + +#define RTFL_OBJ_PRINT(cmd, fmt, ...) \ + RTFL_PRINT ("obj", RTFL_OBJ_VERSION, cmd, fmt, __VA_ARGS__) + +#endif /* __JAVA_MISC_H__ */ |