In profondità

Questo tutorial è dedicato agli sviluppatori, ai cosiddetti "smanettoni" , o a chi, più semplicemente, ha la curiosità di vedere come è stato sviluppato il codice del monitor proposto. Per chi preferisce essere "autodidatta" si rimanda direttamente al download del codice sorgente.
E' richiesta una buona conoscenza del linguaggio Java e dei meccanismi interni della JVM; una buonissima conoscenza del C e del C++, e particolare dimestichezza coi puntatori; è inoltre gradita una infarinatura delle interfacce JNI e JVMPI.
Se non sapete nemmeno di che cosa si sta parlando, non preoccupatevi e continuate a leggere, e prendete questa come l'occasione per imparare.

La DLL jvmmonitor

Questa DLL (Dynamic Linked Library) è stata scritta in C++, e svolge una duplice funzione:

La figura mostra chiaramente come il Profiler si serva degli eventi definiti in JVMPI per costruire il proprio database interno. Questo può essere consultato in qualsiasi momento dal monitor mediante funzioni native, cioè che sfruttano JNI.
Casualmente, o per fortuna, sia JNI che JVMPI usano il meccanismo delle DLL. Il meccanismo per caricarle è differente nei due casi e verrà descritto in seguito.

Che cos'è JNI? JNI sta per Java Native Interface, e permette di chiamare funzioni scritte in C o C++ come se fossero dei normali metodi Java. I vantaggi sono ovviamente le maggiori potenzialità ed efficienza offerte dal C, per contro il codice così scritto non è più portabile. Una guida a JNI è disponibile presso java.sun.com/products/jdk/faq/jni-j2sdk-faq.html. Si spera comunque che il codice di seguito esposto possa servire come esempio autoesplicante per capire i meccanismi fondamentali di questa interfaccia.

Che cos'è JVMPI? JVMPI sta per JVM Profiler Interface, e definisce un set di 36 eventi che la JVM può inviare ad un Profiler, cioè un modulo di codice contenuto in una DLL. Come gestire i dati comunicati da tali eventi è lasciato ovviamente alla fantasia del programmatore. Attualmente l'unica guida a JVMPI è disponibile presso java.sun.com/products/jdk/1.2/docs/guide/jvmpi, e si consiglia di tenerla sottomano.

Inizializzazione del Profiler

L'inizializzazione è forse la fase più delicata e merita pertanto particolare attenzione.
Se si lancia Java con il comando java -Xrunjvmmonitor NomeClasse la JVM caricherà automaticamente la DLL indicata da -Xrun. In particolare verrà subito chiamata la funzione JVM_OnLoad, che quindi rappresenta l'entry-point della DLL. Vediamo, semplificandolo, il codice usato:

  //file jvmmon.cpp
  #include < jvmpi.h >

  extern "C"{

  // var statiche globali 
  static JVMPI_Interface *jvmpi;

  //entry point al caricamento della dll
  JNIEXPORT jint JNICALL
  JVM_OnLoad(JavaVM *jvm, char *options, void *reserved)
    {
    //inizializzazione
    jvm->GetEnv((void**)&jvmpi,JVMPI_VERSION_1);
    //aggancio alla funzione di gestione degli eventi
    jvmpi->NotifyEvent=notifyEvent;
    //abilitazione a ricevere gli eventi indicati
    jvmpi->EnableEvent(JVMPI_EVENT_CLASS_LOAD,NULL);
    jvmpi->EnableEvent(JVMPI_EVENT_JVM_INIT_DONE,NULL);
    jvmpi->EnableEvent(JVMPI_EVENT_JVM_SHUT_DOWN,NULL);
    return JNI_OK; 
    }

  }
Il file jvmpi.h lo trovate in "jdk_dir"/include. Se non c'è probabilmente non avete installato tutto.
La direttiva extern "C" serve al compilatore e al linker del C++. In questo modo le tavole dei simboli generate seguono le specifiche del C anziche del C++, e ciò è richiesto da JVMPI. Omettendo questa istruzione l'entry-point non verrà trovato.

La variabile jvmpi è il cuore del Profiler. Essa è un puntatore ad una struttura dati composta per lo più da puntatori a funzioni. Quindi, tutte le istruzioni jvmpi->NomeFunzione(argomenti) sono in realtà delle chiamate a funzioni predefinite.
L'unica eccezione è costituita da jvmpi->NotifyEvent: questo puntatore va infatti collegato ad una funzione definita dall'utente. Essa sarà la funzione che gestirà gli eventi provenienti dalla JVM: sarà invocata automaticamente al verificarsi di ogni evento di interesse, e riceverà come argomento una struttura dati indicante per l'appunto il tipo di evento e altre sue caratteristiche.
Da questo momento in poi, insomma, il funzionamento della DLL è completamente asincrono: quando un thread Java eseguirà un'azione che determina un evento, la JVM devierà la sua esecuzione sulla funzione jvmpi->NotifyEvent. Il thread eseguirà il codice di tale funzione e, una volta terminato, riprenderà la sua esecuzione nel punto in cui si era interrotto. E' quindi più che mai naturale che più thread Java abbiano contemporaneamente il proprio program-counter all'interno della funzione jvmpi->NotifyEvent.

Si ricorda che inizialmente tutti i tipi di eventi sono disabilitati, e bisogna perciò dire alla JVM quali si desidera ricevere, utilizzando la funzione jvmpi->EnableEvent(flag).

continua