Logo Search packages:      
Sourcecode: jmp version File versions  Download package

jvm.c

/*
    jvm simulator for jvmpi-profiler testing
    Copyright (C) 2003 Robert Olofsson
 
    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.
 
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
 
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
    Robert Olofsson
    robo@khelekore.org
*/
/*
    This simulator is heavily influenced by the simulator.cpp found in
    Mike's Java Profiler
    Copyright (C) 2002  Michael J. Bresnahan
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <jni.h>
#include <jvmpi.h>
#include <pthread.h>
#include <jvm.h>

/* Compile with -DJMPDEBUG to get lots of traces, can be helpful sometimes. */
#ifndef JVMDEBUG
#define trace(a) 
#define trace2(a,b) 
#define trace3(a,b,c)
#define trace4(a,b,c,d)
#else
#define trace(a) fprintf (stderr, a); fflush (stderr)
#define trace2(a,b) fprintf (stderr, a, b); fflush (stderr)
#define trace3(a,b,c) fprintf (stderr, a, b, c); fflush (stderr)
#define trace4(a,b,c,d) fprintf (stderr, a, b, c, d); fflush (stderr)
#endif /* JVMDEBUG */


static JVMPI_Interface* jvmpi;

struct _JVMPI_RawMonitor {
    char* name;
    pthread_mutex_t mut;
    pthread_cond_t* condition;
};

jint JNICALL GetEnv (JavaVM *vm, void **penv, jint version) {
    trace4 ("GetEnv (%p, %p, %d)\n", vm, penv, version);
    *penv = (void*)jvmpi;
    return 0;
}

JVMPI_RawMonitor RawMonitorCreate (char *lock_name) {
    JVMPI_RawMonitor r;
    trace2 ("RawMonitorCreate (%s)\n", lock_name);
    r = (JVMPI_RawMonitor)malloc (sizeof (*r));
    r->name = strdup (lock_name);
    r->condition = 0; 
    pthread_mutex_init (&r->mut, NULL);
    return r;
}

void RawMonitorEnter (JVMPI_RawMonitor r) {
    trace2 ("RawMonitorEnter (%s)\n", r->name);
    pthread_mutex_lock (&r->mut);
}

void RawMonitorExit (JVMPI_RawMonitor r) {
    trace2 ("RawMonitorExit (%s)\n", r->name);
    pthread_mutex_unlock (&r->mut);
}

void RawMonitorDestroy (JVMPI_RawMonitor r) {
    trace2 ("RawMonitorDestroy (%s)\n", r->name);
    if (r) {
      if (r->condition) {
          pthread_cond_destroy (r->condition);
          free (r->condition);
      }
      pthread_mutex_destroy (&r->mut);
      free (r->name);
      free (r);
    }
}

void RawMonitorWait (JVMPI_RawMonitor r, jlong milis) {
    trace2 ("RawMonitorWait (%s)\n", r->name);
    if (r->condition == NULL) {
      int ret;
      r->condition = malloc(sizeof (*r->condition));
      ret = pthread_cond_init (r->condition, NULL);
    }
    pthread_cond_wait (r->condition, &r->mut);
}

void RawMonitorNotifyAll (JVMPI_RawMonitor r) {
    trace2 ("RawMonitorNotifyAll (%s)\n", r->name);
    if (r->condition == NULL) {
      trace2 ("RawMonitorNotifyAll (%s) on empty condition\n", r->name);
      return;
    }
    pthread_cond_signal (r->condition);
}

/** get the thread status. 
 * TODO: implement for real, currently always returns JVMPI_THREAD_RUNNABLE.
 */
jint GetThreadStatus (JNIEnv* env_id) {
    return JVMPI_THREAD_RUNNABLE;
}

jint EnableEvent (jint event_type, void *arg) {
    trace3 ("EnableEvent (%d, %p)\n", event_type, arg);
    return JVMPI_SUCCESS;
}
jint DisableEvent (jint event_type, void *arg) {
    trace3 ("DisableEvent (%d, %p)\n", event_type, arg);
    return JVMPI_SUCCESS;
}
jint RequestEvent (jint event_type, void *arg) {
    trace3 ("RequestEvent (%d, %p)\n", event_type, arg);
    return JVMPI_SUCCESS;
}

void DisableGC (void) {
    trace ("DisableGC ()\n");
}
void EnableGC (void) {
    trace ("EnableGC ()\n");
}
void RunGC (void) {
    trace ("RunGC ()\n");
    call_gc_start ((JNIEnv*)pthread_self ());
    call_gc_finish ((JNIEnv*)pthread_self (), 4711, 
                4711 * 8, 16 * 1024 * 1024);
}

struct local_storage; 
struct local_storage {
    JNIEnv* env_id;
    void*   ptr;
    struct local_storage* next;
};

struct local_storage* store = NULL;

void SetThreadLocalStorage (JNIEnv *env_id, void *ptr) {
    struct local_storage* ls = store;
    trace3 ("SetThreadLocalStorage (%p, %p)\n", env_id, ptr);
    while (ls != NULL) {
      if (ls->env_id == env_id) {
          ls->ptr = ptr;
          return;
      }
      ls = ls->next;
    }
    ls = (struct local_storage*)malloc (sizeof (*ls));
    ls->env_id = env_id;
    ls->ptr = ptr;
    ls->next = store;
    store = ls;
}

void* GetThreadLocalStorage (JNIEnv *env_id) {
    struct local_storage* ls = store;
    trace2 ("GetThreadLocalStorage (%p)\n", env_id);
    while (ls != NULL) {
      if (ls->env_id == env_id) {
          return ls->ptr;
      }
      ls = ls->next;
    }
    return NULL;
}

static void FreeThreadLocalStorage () {
    struct local_storage* ls = store;
    struct local_storage* next;
    while (ls) {
      next = ls->next;
      free (ls);
      ls = next;
    }
}

static jint CreateSystemThread (char* name, jint priority, void (*f)(void*)) {
    pthread_t thread;
    int status;
    status = pthread_create (&thread, NULL, (void*)(*f), NULL);
    if (status == 0)
      return JNI_OK;
    return JNI_ERR;
}

void call_method_entry (JNIEnv* thread_env_id, jmethodID mid) {
    JVMPI_Event event;
    event.event_type = JVMPI_EVENT_METHOD_ENTRY;
    event.env_id = thread_env_id;
    event.u.method.method_id = mid;
    jvmpi->NotifyEvent (&event);
}

void call_method_exit (JNIEnv* thread_env_id, jmethodID mid) {
    JVMPI_Event event;
    event.event_type = JVMPI_EVENT_METHOD_EXIT;
    event.env_id = thread_env_id;
    event.u.method.method_id = mid;
    jvmpi->NotifyEvent (&event);
}

void call_object_alloc (JNIEnv* thread_env_id, jint arena_id, 
                  jobjectID class_id, jint is_array, 
                  jint size, jobjectID obj_id) {
    JVMPI_Event event;
    event.event_type = JVMPI_EVENT_OBJECT_ALLOC;
    event.env_id = thread_env_id;
    event.u.obj_alloc.arena_id = arena_id;
    event.u.obj_alloc.class_id = class_id;
    event.u.obj_alloc.is_array = is_array;
    event.u.obj_alloc.size = size;
    event.u.obj_alloc.obj_id = obj_id;
    jvmpi->NotifyEvent (&event);    
}

void call_object_move (JNIEnv* thread_env_id, 
                   jint arena_id, jobjectID obj_id, 
                   jint new_arena_id, jobjectID new_obj_id) {
    JVMPI_Event event;
    event.event_type = JVMPI_EVENT_OBJECT_MOVE;
    event.u.obj_move.arena_id = arena_id;
    event.u.obj_move.obj_id = obj_id;
    event.u.obj_move.new_arena_id = new_arena_id;
    event.u.obj_move.new_obj_id = new_obj_id;    
    jvmpi->NotifyEvent (&event);    
}
                         
void call_object_free (JNIEnv* thread_env_id, 
                   jobjectID obj_id) {
    JVMPI_Event event;
    event.event_type = JVMPI_EVENT_OBJECT_FREE;
    event.u.obj_free.obj_id = obj_id;
    jvmpi->NotifyEvent (&event);    
}

void call_gc_start (JNIEnv* thread_env_id) {
    JVMPI_Event event;
    event.event_type = JVMPI_EVENT_GC_START;
    event.env_id = thread_env_id;
    jvmpi->NotifyEvent (&event); 
}

void call_gc_finish (JNIEnv* thread_env_id, 
                 jlong used_objects, 
                 jlong used_object_space, 
                 jlong total_object_space) {
    JVMPI_Event event;
    event.event_type = JVMPI_EVENT_GC_FINISH;
    event.env_id = thread_env_id;
    event.u.gc_info.used_objects = used_objects;
    event.u.gc_info.used_object_space = used_object_space;
    event.u.gc_info.total_object_space = total_object_space;
    jvmpi->NotifyEvent (&event);     
}

void call_arena_new (JNIEnv* thread_env_id, jint arena_id, 
                 char* arena_name) {
    JVMPI_Event event;
    event.event_type = JVMPI_EVENT_ARENA_NEW;
    event.env_id = thread_env_id;
    event.u.new_arena.arena_id = arena_id;
    event.u.new_arena.arena_name = arena_name;
    jvmpi->NotifyEvent (&event);
}

void call_arena_delete (JNIEnv* thread_env_id, jint arena_id) {
    JVMPI_Event event;
    event.event_type = JVMPI_EVENT_ARENA_DELETE;
    event.env_id = thread_env_id;
    event.u.delete_arena.arena_id = arena_id;
    jvmpi->NotifyEvent (&event);
}

void call_data_dump_request (JNIEnv* thread_env_id) {
    JVMPI_Event event;
    event.event_type = JVMPI_EVENT_DATA_DUMP_REQUEST;
    event.env_id = thread_env_id;   
    jvmpi->NotifyEvent (&event);
}

void call_data_reset_request (JNIEnv* thread_env_id) {
    JVMPI_Event event;
    event.event_type = JVMPI_EVENT_DATA_RESET_REQUEST;
    event.env_id = thread_env_id;
    jvmpi->NotifyEvent (&event);
}

void call_monitor_contended_function (JNIEnv* thread_env_id, jint event_type, jobjectID jid) {
    JVMPI_Event event;
    event.event_type = event_type;
    event.env_id = thread_env_id;
    event.u.monitor.object = jid;
    jvmpi->NotifyEvent (&event);
}

void call_monitor_contended_enter (JNIEnv* thread_env_id, jobjectID jid) {
    call_monitor_contended_function (thread_env_id, JVMPI_EVENT_MONITOR_CONTENDED_ENTER, jid);
}

void call_monitor_contended_entered (JNIEnv* thread_env_id, jobjectID jid) {
    call_monitor_contended_function (thread_env_id, JVMPI_EVENT_MONITOR_CONTENDED_ENTERED, jid);
}

void call_monitor_contended_exit (JNIEnv* thread_env_id, jobjectID jid) {
    call_monitor_contended_function (thread_env_id, JVMPI_EVENT_MONITOR_CONTENDED_EXIT, jid);
}

static void call_monitor_wait_event (JNIEnv* thread_env_id, jint event_type, 
                             jobjectID jid, jlong timeout) {
    JVMPI_Event event;
    event.event_type = event_type;
    event.env_id = thread_env_id;
    event.u.monitor_wait.object = jid;    
    event.u.monitor_wait.timeout = timeout;
    jvmpi->NotifyEvent (&event);    
}

void call_monitor_wait (JNIEnv* thread_env_id, jobjectID jid, jlong timeout) {
    call_monitor_wait_event (thread_env_id, JVMPI_EVENT_MONITOR_WAIT, jid, timeout);
}

void call_monitor_waited (JNIEnv* thread_env_id, jobjectID jid, jlong waited) {
    call_monitor_wait_event (thread_env_id, JVMPI_EVENT_MONITOR_WAITED, jid, waited);
}

/** read all data in given file into a newly allocated char*. Remeber to free. */
char* get_file_buffer (char* file_name, int* size) {
    char* buf;
    struct stat stat_buf;
    FILE* fp = fopen (file_name, "r");
    if (fp == NULL) {
      fprintf (stderr, "%s not found, ignoring dump test\n", file_name);
      return NULL;
    }
    stat (file_name, &stat_buf);
    buf = malloc (stat_buf.st_size);
    fread (buf, 1, stat_buf.st_size, fp);
    fclose (fp);
    *size = stat_buf.st_size;
    return buf;
}

void call_monitor_dump (JNIEnv* thread_env_id) {
    JVMPI_Event event;
    int size;
    char* file_name = "monitor_dump.bin";
    char* buf = get_file_buffer (file_name, &size);
    if (buf == NULL)
      return;
    event.event_type = JVMPI_EVENT_MONITOR_DUMP;
    event.env_id = thread_env_id;
    event.u.monitor_dump.begin = buf;
    event.u.monitor_dump.end = buf + size;
    event.u.monitor_dump.num_traces = 0;
    event.u.monitor_dump.traces = NULL;
    event.u.monitor_dump.threads_status = NULL;    
    jvmpi->NotifyEvent (&event);

    free (buf);
}

void call_heap_dump (JNIEnv* thread_env_id, jint dump_level, char* file_name) {
    JVMPI_Event event;
    int size;
    char* buf = get_file_buffer (file_name, &size);
    if (buf == NULL)
      return;
    event.event_type = JVMPI_EVENT_HEAP_DUMP;
    event.env_id = thread_env_id;
    event.u.heap_dump.dump_level = dump_level;
    event.u.heap_dump.begin = buf;
    event.u.heap_dump.end = buf + size;
    event.u.heap_dump.num_traces = 0;
    event.u.heap_dump.traces = NULL;
    jvmpi->NotifyEvent (&event);
    free (buf);    
}

void call_heap_dump_0 (JNIEnv* thread_env_id) {
    call_heap_dump (thread_env_id, JVMPI_DUMP_LEVEL_0, "heap_dump_0.bin");
}

void call_heap_dump_2 (JNIEnv* thread_env_id) {
    call_heap_dump (thread_env_id, JVMPI_DUMP_LEVEL_2, "heap_dump_2.bin");
}

void call_object_dump (JNIEnv* thread_env_id) {
    JVMPI_Event event;
    int size;
    char* buf = get_file_buffer ("object_dump.bin", &size);
    if (buf == NULL)
      return;
    event.event_type = JVMPI_EVENT_OBJECT_DUMP;
    event.env_id = thread_env_id;
    event.u.object_dump.data_len = size;
    event.u.object_dump.data = buf;
    jvmpi->NotifyEvent (&event);
    free (buf);
}

int main (int argc, char **argv) {
    void* handle = 0;
    char* error = 0;
    char* options = NULL;
    
    struct JNIInvokeInterface_* jvm;
    JNIEXPORT jint JNICALL (*onload)(JavaVM *, char *, void *);

    if (argc < 2) {
      fprintf (stderr, "must give library to test...\n");
      exit (-1);
    }
    if (argc > 2) {
      options = argv[2];
    }

    handle = dlopen (argv[1], RTLD_LAZY);
    if (!handle) {
      fputs (dlerror (), stderr);
      exit(1);
    }

    onload = dlsym (handle, "JVM_OnLoad");
    if ((error = dlerror ()) != NULL)  {
      fprintf (stderr, "%s\n", error);
      exit(1);
    }
    
    jvm = (struct JNIInvokeInterface_*)malloc (sizeof (*jvm));
    jvm->GetEnv = GetEnv;
    jvmpi = (JVMPI_Interface*)malloc (sizeof (*jvmpi));
    jvmpi->RawMonitorCreate = RawMonitorCreate;
    jvmpi->RawMonitorEnter = RawMonitorEnter;
    jvmpi->RawMonitorExit = RawMonitorExit;
    jvmpi->RawMonitorWait = RawMonitorWait;
    jvmpi->RawMonitorNotifyAll = RawMonitorNotifyAll;
    jvmpi->RawMonitorDestroy = RawMonitorDestroy;
    jvmpi->GetThreadStatus = GetThreadStatus;
    jvmpi->EnableEvent = EnableEvent;
    jvmpi->DisableEvent = DisableEvent;
    jvmpi->RequestEvent = RequestEvent;
    jvmpi->DisableGC = DisableGC;
    jvmpi->EnableGC = EnableGC;
    jvmpi->RunGC = RunGC;
    jvmpi->SetThreadLocalStorage = SetThreadLocalStorage;
    jvmpi->GetThreadLocalStorage = GetThreadLocalStorage;
    jvmpi->CreateSystemThread = CreateSystemThread;
    
    trace ("calling JVM_OnLoad (...)\n");
    onload ((JavaVM*)&jvm, options, NULL);
    trace ("returned from JVM_OnLoad (...)\n");

    /* this needs lots of jni for jmp testing...*/
    {
      JVMPI_Event event;
      event.event_type = JVMPI_EVENT_JVM_INIT_DONE;
      event.env_id = NULL;
      jvmpi->NotifyEvent (&event);  
    }

    /* Alloc an arena */
    call_arena_new (NULL, 1, "arena_1"); /* TODO: do we need a valid JNIEnv*  here? */

    /** start a thread and end it. */
    {
      /** start a thread.. */
      JVMPI_Event event;
      JNIEnv* thread_env_id;
      char* class_name = "foo";
      char* source_name = "foo.java";
      int i = 0;
      int j = 0;
      trace ("starting a thread...\n");
      thread_env_id = (JNIEnv*)malloc (sizeof (*thread_env_id));

      event.event_type = JVMPI_EVENT_THREAD_START;
      event.env_id = NULL;
      event.u.thread_start.thread_name = "thread1";
      event.u.thread_start.group_name = "group1";
      event.u.thread_start.parent_name = "parent1";
      event.u.thread_start.thread_id = NULL;
      event.u.thread_start.thread_env_id = thread_env_id;
      jvmpi->NotifyEvent (&event);

      
      /** Test class load, (method_entry, method_exit)*X, class unload... */
      trace ("loading a class...\n");
      event.event_type = JVMPI_EVENT_CLASS_LOAD;
      event.env_id = thread_env_id;
      event.u.class_load.class_name = class_name;
      event.u.class_load.source_name = source_name;
      event.u.class_load.num_interfaces = 0;
      event.u.class_load.num_methods = 4;
      event.u.class_load.methods = (JVMPI_Method*)malloc (sizeof (JVMPI_Method) * event.u.class_load.num_methods);
      event.u.class_load.methods[i].method_id = (jmethodID)i;
      event.u.class_load.methods[i].method_name = "a";
      event.u.class_load.methods[i].start_lineno = 0;
      event.u.class_load.methods[i].end_lineno = 0;
      event.u.class_load.methods[i++].method_signature = "()V";   
      event.u.class_load.methods[i].method_id = (jmethodID)i;
      event.u.class_load.methods[i].method_name = "b";
      event.u.class_load.methods[i].start_lineno = 0;
      event.u.class_load.methods[i].end_lineno = 0;
      event.u.class_load.methods[i++].method_signature = "()V";
      event.u.class_load.methods[i].method_id = (jmethodID)i;
      event.u.class_load.methods[i].method_name = "c";
      event.u.class_load.methods[i].start_lineno = 0;
      event.u.class_load.methods[i].end_lineno = 0;
      event.u.class_load.methods[i++].method_signature = "(IJZF)V";
      event.u.class_load.methods[i].method_id = (jmethodID)i;
      event.u.class_load.methods[i].method_name = "d";
      event.u.class_load.methods[i].start_lineno = 0;
      event.u.class_load.methods[i].end_lineno = 0;
      event.u.class_load.methods[i++].method_signature = "(II[I)V";
      event.u.class_load.num_static_fields = 0;
      event.u.class_load.statics = NULL;
      event.u.class_load.num_instance_fields = 0;
      event.u.class_load.instances = NULL;
      event.u.class_load.class_id = 0;
      jvmpi->NotifyEvent (&event); 
      
      free (event.u.class_load.methods);
      
      trace ("doing some method_entry, method_exit...\n");
      event.env_id = thread_env_id;
      for (i = 0; i < 10; i++) {       
          call_method_entry (thread_env_id, (jmethodID)0);
          for (j = 0; j < 100; j++) {
            call_object_alloc (thread_env_id, 0, (jobjectID)0, 0, 8, (jobjectID)((i << 16) + j));
          }
          
          for (j = 0; j < 10; j++) {
            call_method_entry (thread_env_id, (jmethodID)1);
            if (j < 6) {
                call_method_entry (thread_env_id, (jmethodID)2);
                call_method_exit (thread_env_id, (jmethodID)2);
            }
            call_method_exit (thread_env_id, (jmethodID)1);
            if (j > 4) {
                call_method_entry (thread_env_id, (jmethodID)3);
                call_method_exit (thread_env_id, (jmethodID)3);
            }
          } 
          call_method_exit (thread_env_id, (jmethodID)0);
      }
      
      /* data dump and reset, optional features... */
      call_data_dump_request (thread_env_id);
      call_data_reset_request (thread_env_id);
      
      /* test monitors contention a bit... */
      call_monitor_contended_enter (thread_env_id, (jobjectID)0);
      call_monitor_contended_entered (thread_env_id, (jobjectID)0);
      call_monitor_contended_exit (thread_env_id, (jobjectID)0);
      
      /* test monitor wait */
      call_monitor_wait (thread_env_id, (jobjectID)0, 0);
      call_monitor_waited (thread_env_id, (jobjectID)0, 10);

      /* test gc */
      call_gc_start (thread_env_id);
      call_object_move (thread_env_id, 0, (jobjectID)0, 0, (jobjectID)1000);
      for (j = 1; j < 100; j++) {
          call_object_free (thread_env_id, (jobjectID)j);
      }
      call_gc_finish (thread_env_id, 901, 7208, 16 * 1024 * 1024);
      
      trace ("unloading class...\n");
      event.event_type = JVMPI_EVENT_CLASS_UNLOAD;
      event.env_id = thread_env_id;
      event.u.class_unload.class_id = 0;        
      jvmpi->NotifyEvent (&event);

      /* test different dumps. */
      call_monitor_dump (thread_env_id);
      call_heap_dump_0 (thread_env_id);
      call_heap_dump_2 (thread_env_id);
      call_object_dump (thread_env_id);

      /* remove arena */
      call_arena_delete (thread_env_id, 1);     

      /** end the thread.. */
      trace ("ending thread...\n");
      event.env_id = thread_env_id;
      event.event_type = JVMPI_EVENT_THREAD_END;
      jvmpi->NotifyEvent (&event);
      
      free (thread_env_id);
      trace ("simple thread test ok...\n");
    }
    
    sleep (3);

    /** Send the shut down. */
    {
      JVMPI_Event event;
      trace ("sending shut down event to close down...\n");
      event.event_type = JVMPI_EVENT_JVM_SHUT_DOWN;
      event.env_id = NULL;
      jvmpi->NotifyEvent (&event);  
      trace ("shut down sent...\n");
    }

    trace ("freeing simulator structures...\n");
    FreeThreadLocalStorage ();
    free (jvmpi);
    free (jvm);
    dlclose(handle);
    trace ("exiting...\n");
    return 0;
}

Generated by  Doxygen 1.6.0   Back to index