Pohled pod kapotu JVM - sledování činnosti virtuálního stroje Javy přes rozhraní JVM TI

Pavel Tišnovský 6. 11. 2012

V dnešní části seriálu o programovacím jazyce Java si popíšeme základní vlastnosti rozhraní JVM TI, pomocí něhož je možné sledovat a taktéž řídit činnost tohoto virtuálního stroje. S využitím JVM TI je například možné přečíst obsah celého heapu, detekovat volání metod, detekovat vzniklé výjimky atd.

Obsah

1. Pohled pod kapotu JVM – sledování činnosti virtuálního stroje Javy přes rozhraní JVM TI (Java Virtual Machine Tools Interface)

2. První demonstrační příklad – kostra jednoduchého JVM TI agenta

3. Překlad agenta

4. Registrace agenta ve spouštěném virtuálním stroji

5. Druhý demonstrační příklad – kontrola, zda je verze rozhraní JVM TI korektní

6. Registrace callback funkcí v JVM TI

7. Nastavení režimů notifikace

8. Třetí demonstrační příklad: agent s dvojicí zaregistrovaných callback funkcí

9. Odkazy na Internetu

1. Pohled pod kapotu JVM – sledování činnosti virtuálního stroje Javy přes rozhraní JVM TI (Java Virtual Machine Tools Interface)

V dnešní části seriálu o programovacím jazyku Java i o jeho virtuálním stroji si řekneme základní informace o rozhraní JVM TI, neboli celým názvem Java Virtual Machine Tools Interface, které lze využít pro sledování činnosti virtuálního stroje Javy i pro jeho částečné řízení. Toto rozhraní, které bylo poprvé implementováno v J2SE 5.0 může být využito k provádění mnoha různých operací. Například je možné sledovat práci správce paměti (GC – garbage collector), přistupovat k objektům uloženým na heapu, zjišťovat volané metody, detekovat a sledovat překlad bajtkódu JVM do nativního strojového kódu dané platformy, nastavovat a posléze i využívat breakpointy, detekovat výjimky v javovských aplikacích (a to i ty výjimky, které jsou zachycené v bloku catch) atd. JVM TI je využíváno debuggery a profilery, které potřebují přistupovat k běžící JVM, ovšem vlastnosti tohoto rozhraní lze využít i v dalších typech nástrojů, například pro zjišťování pokrytí kódu testy, detekci přístupu k určitým souborům apod.

Rozhraní JVM TI může být využíváno takzvanými agenty, což jsou nativní knihovny, které jsou v tom nejjednodušším případě načteny při startu JVM a běží ve stejném procesu, jako samotná JVM (jak se agent integruje do JVM si řekneme v navazujících kapitolách). Agenti mohou přes rozhraní JVM TI oboustranně komunikovat s virtuálním strojem Javy. Komunikace směrem agent→JVM probíhá jednoduše voláním funkcí nabízených rozhraním, zpětná komunikace JVM→agent je zabezpečena pomocí takzvaných callback funkcí, které musí být nejdříve zaregistrovány pro různé typy událostí, které mohou ve virtuálním stroji nastat (mezi událost může například patřit spuštění správce paměti, vznik výjimky atd.). Vzhledem k tomu, že agenti musí být překládáni do nativní knihovny, používá se pro jejich implementaci většinou programovací jazyk C či C++ (popř. jakýkoli jiný jazyk podporující céčkovskou konvenci volání funkcí), ovšem samotný agent je většinou poměrně kompaktní knihovna nabízející své vlastní rozhraní externím procesům – takto lze například realizovat debugger nebo jednoduchý monitorovací nástroj.

2. První demonstrační příklad – kostra jednoduchého JVM TI agenta

V této kapitole si ukážeme zdrojový kód kostry jednoduchého JVM TI agenta. Samotný zdrojový kód je psaný v céčku a obsahuje pouze dvě funkce nazvané Agent_OnLoad a Agent_OnUnload. První z těchto funkcí je zavolána ve chvíli, kdy je agent načten do JVM a obvykle se zde provádí inicializace agenta, popř. test, zda je rozhraní nabízené virtuálním strojem Javy s agentem kompatibilní (může se totiž stát, že JVM nenabízí plnohodnotné rozhraní JVM TI, ovšem při použití Oracle JDK 6/7 či OpenJDK 6/7 k problémům nedochází). Funkce Agent_OnLoad by měla vrátit hodnotu JNI_OK v případě korektní inicializace. Naproti tomu funkce nazvaná Agent_OnUnload je zavolána ve chvíli odpojování agenta od virtuálního stroje Javy, což typicky nastává při ukončování samotné JVM. V této funkci se například může nacházet kód pro uzavření logovacích souborů, uzavření socketů (pokud agent nabízí své vlastní rozhraní přes sockety, což je velmi časté) atd.

Při pohledu na zdrojový kód kostry agenta je patrné, že se zde používá mnoho symbolů, které nepatří do standardního céčka: JNIEXPORT, jint, JNICALL, JavaVM apod. Tyto symboly jsou deklarovány v hlavičkovém souboru jvmti.h, popř. v hlavičkových souborech vkládaných do jvmti.h. Hlavičkový soubor jvmti.h by měl být součástí instalace JDK a nachází se (v případě Linuxových distribucí) v adresáři /usr/lib/jvm/{jméno_JDK}/include, kde se za {jméno_JDK} doplňuje konkrétní název adresáře, v němž je daná JDK nainstalována. Ve skutečnosti totiž může být v jednom systému nainstalováno více JDK a v současnosti, kdy se postupně přechází z JDK 6 na JDK 7, je to poměrně obvyklá situace. Následuje výpis zdrojového kódu prvního demonstračního příkladu:

/*
 * Kostra jednoducheho JVM TI agenta.
 */
 
/* Nutno nastavit cestu pres volbu -Icesta_k_jvm */
#include <jvmti.h>
 
#define AGENT_NAME "Agent1:"
 
/*
 * Vypis zpravy na standardni vystup.
 */
#define MSG(message) puts(AGENT_NAME " " message)
 
/*
 * Funkce zavolana ve chvili nacitani agenta do JVM.
 */
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved)
{
    MSG("Agent_OnLoad");
    return JNI_OK;
}
 
/*
 * Funkce zavolana ve chvili odstranovani agenta z JVM.
 */
JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm)
{
    MSG("Agent_OnUnload");
}
 
/*
 * finito
 */

3. Překlad agenta

V této kapitole si řekneme, jakým způsobem se zdrojový kód jednoduchého agenta přeloží a jak se posléze agent zaregistruje do virtuálního stroje Javy. Pro překlad (na Linuxu) je nutné mít nainstalován GNU toolchain, především překladač céčka, linker a standardní céčkové knihovny i s jejich hlavičkovými soubory. Při překladu/linkování je nutné přepínačem -shared specifikovat, že se má vytvořit sdílená knihovna, nikoli spustitelný nativní kód (to by ve skutečnosti ani nebylo možné, protože kód agenta neobsahuje funkci main). Dále je nutné přes volbu -I specifikovat cestu ke hlavičkovému souboru jvmti.h, o němž jsme se zmiňovali v předchozí kapitole. Poslední důležitým přepínačem je -o libagent1.so, protože přeložená sdílená knihovna by měla mít název odpovídající schématu lib{název}.so:

gcc -Wall -ansi -I/usr/lib/jvm/java-1.6.0-openjdk/include/ -shared -o libagent1.so agent1.c

V závislosti na konfiguraci systému se někdy musí použít i přepínač -fPIC:

gcc -Wall -ansi -fPIC -I/usr/lib/jvm/java-1.6.0-openjdk/include/ -shared -o libagent1.so agent1.c

4. Registrace agenta ve spouštěném virtuálním stroji

Pokud překlad proběhne v pořádku, měl by se v aktivním adresáři vytvořit soubor s názvem libagent1.so, který by měl obsahovat minimálně dva externí symboly s názvy odpovídajícími oběma deklarovaným funkcím: Agent_OnLoad a Agent_OnUnload. O tom se ostatně můžeme snadno přesvědčit:

nm -g --defined-only libagent1.so
 
0000046c T Agent_OnLoad
00000485 T Agent_OnUnload
0000200c A __bss_start
0000200c A _edata
00002014 A _end
000004d8 T _fini
00000348 T _init

Nyní si již můžeme vyzkoušet agenta nahrát do virtuálního stroje Javy při startu JVM. Nejprve vytvoříme a přeložíme nějakou velmi jednoduchou javovskou aplikaci, například variaci na klasický „Hello world“:

public class Test {
 
    public static void main(String[] args) {
        System.out.println("Test.main() called");
    }
 
}

Pokud jsou oba přeložené soubory, tj. jak libagent1.so, tak i Test.class, uloženy v jednom adresáři, máme dvě možnosti, jak agenta spustit v rámci procesu virtuálního stroje Javy. První způsob využívá přepínač -agentpath, kterému se předá cesta k nativní knihovně:

java -agentpath:./libagent1.so Test

Druhý způsob využívá přepínač -agentlib, za nějž se zapisuje jméno knihovny bez prefixu „lib“ a koncovky „.so“. Knihovna je hledána na cestách uložených v proměnné prostředí LD_LIBRARY_PATH, tj. buď musíme naši knihovnu libagent1.so nakopírovat do správného adresáře (/usr/lib atd.) nebo musíme proměnnou prostředí předefinovat:

export LD_LIBRARY_PATH=.
java -agentlib:agent1 Test

Nezávisle na tom, jak byla JVM a agent spuštěny, měl by být text zapsaný na standardní výstup v obou případech shodný:

Agent1: Agent_OnLoad
Test.main() called
Agent1: Agent_OnUnload

5. Druhý demonstrační příklad – kontrola, zda je verze rozhraní JVM TI korektní

V předchozím textu jsme si řekli, že ve funkci Agent_OnLoad se většinou provádí inicializace agenta a že se zde většinou taktéž nachází kontrola, zda virtuální stroj Javy plně podporuje rozhraní JVM TI požadované verze. Pro zjištění, zda implementace JVM TI nabízená běžícím virtuálním strojem Javy skutečně odpovídá požadované verzi, se používá funkce nazvaná GetEnv s následující hlavičkou:

jint GetEnv(JavaVM *vm, void **env, jint version);

Této funkci se předává trojice parametrů – ukazatel na strukturu JavaVM, v níž jsou uloženy, jak uvidíme dále, ukazatele na funkce nabízené JVM TI. Druhým parametrem je ukazatel na strukturu jvmtiEnv: tato struktura má být funkcí GetEnv naplněna. Do třetího parametru se předává požadovaná verze rozhraní JVM TI, což je v současnosti verze 1 představovaná konstantou JVMTI_VERSION1_0. Tato funkce může vrátit tři hodnoty: JNI_EDETACHED (chyba: vlákno není připojeno k virtuálnímu stroji), JNI_EVERSION (chyba: nepodporovaná verze JVM TI) a JNI_OK (ok: korektní verze JVM TI).

Aby to však nebylo tak jednoduché, nelze funkci GetEnv zavolat přímo, protože (pojmenovaný) ukazatel na ni je uložen pouze ve struktuře nazvané struct JNINativeInterface. Ve skutečnosti je totiž, alespoň při použití programovacího jazyka C, první parametr předávaný funkci Agent_OnLoad ukazatelem na strukturu nazvanou JNINativeInterface_, která obsahuje množství ukazatelů na různé důležité funkce:

struct JNINativeInterface_ {
    void *reserved0;
    void *reserved1;
    void *reserved2;
    void *reserved3;
    jint (JNICALL *GetVersion)(JNIEnv *env);
    jclass (JNICALL *DefineClass) (JNIEnv *env, const char *name, jobject loader, const jbyte *buf, jsize len);
    jclass (JNICALL *FindClass) (JNIEnv *env, const char *name);
 
...
 
}

V podstatě se tedy jedná o implementaci tabulky virtuálních metod přímo v céčku :-) Vraťme se nyní ke zjištění verze JVM TI. Jakmile máme ukazatel na strukturu JNINativeInterface_, můžeme přes operátor → zavolat jakoukoli funkci, jejíž ukazatel je v této struktuře použit:

jint = result = (*jvm)->GetEnv(jvm, (void **) &jvmti, JVMTI_VERSION_1_0);

Výsledkem volání musí být hodnota JNI_OK a ukazatel vrácený ve druhém parametru nesmí být roven NULL. Pokud alespoň jedna z těchto podmínek není splněna, nemá agent k dispozici správné rozhraní JVM TI a jeho další činnost tudíž nemusí být korektní – je tedy vhodné práci agenta ukončit tak, že funkce Agent_OnLoad vrátí chybovou hodnotu namísto JNI_OK. To jsou již všechny informace potřebné pro naprogramování druhého demonstračního příkladu:

/*
 * Kostra jednoducheho JVM TI agenta.
 * s kontrolou verze rozhrani JVM TI. 
 */
 
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
/* Nutno nastavit cestu pres volbu -Icesta_k_jvm */
#include <jvmti.h>
 
#define AGENT_NAME "Agent2:"
 
/*
 * Vypis zpravy na standardni vystup.
 */
#define MSG(message) puts(AGENT_NAME " " message)
 
/*
 * Funkce zavolana ve chvili nacitani agenta do JVM.
 */
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved)
{
    jvmtiEnv *jvmti = NULL;
    jint result;
 
    MSG("Agent_OnLoad");
    result = (*jvm)->GetEnv(jvm, (void **) &jvmti, JVMTI_VERSION_1_0);
    if (result != JNI_OK || jvmti == NULL)
    {
        printf("ERROR: Unable to access JVMTI Version 1 (0x%x),"
                " is your J2SE a 1.5 or newer version? JNIEnv's GetEnv() returned %d\n",
                JVMTI_VERSION_1, (int)result);
        return result;
    }
    MSG("JVM TI version is correct");
    return JNI_OK;
}
 
/*
 * Funkce zavolana ve chvili odstranovani agenta z JVM.
 */
JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm)
{
    MSG("Agent_OnUnload");
}
 
/*
 * finito
 */

6. Registrace callback funkcí v JVM TI

Aby náš testovací agent prováděl nějakou rozumnou činnost, musíme při jeho startu a inicializaci zaregistrovat callback funkce, tj. funkce, které budou zavolány přímo virtuálním strojem Javy při výskytu nějaké události, pro něž je daná funkce zaregistrována. Prozatím známe velmi málo informací o rozhraní JVM TI, proto si dnes ukážeme použití pouze tří callback funkcí. Bude se jednat o funkci vyvolanou ihned po inicializaci virtuálního stroje, funkci zavolanou při ukončování práce virtuálního stroje a konečně funkci, která bude zavolána při vstupu do jakékoli javovské metody (to je asi nejužitečnější část agenta, i když prozatím nebudeme moci zjistit jméno/signaturu metody ani jméno její třídy). Registrace callback funkcí je relativně snadná – postačuje pouze zavolat funkci SetEventCallbacks, které se předá ukazatel na strukturu obsahující ukazatele na všechny callback funkce. Pokud se v dané struktuře použije namísto korektního ukazatele hodnota NULL, povede pokus o volání takovéto funkce k detekovatelné chybě. Hlavička funkce SetEventCallbacks vypadá takto:

SetEventCallbacks(jvmti, callbacks, sizeof(callbacks));

Tuto funkci opět není možné volat přímo, ale musí se použít nepřímé volání přes ukazatel uložený ve struktuře jvmtiEnv. Ukazatel na tuto strukturu již dovedeme získat – použije se funkce GetEnv, která tuto strukturu vrátí ve svém druhém parametru (musí se samozřejmě předávat opět formou ukazatele). U funkce SetEventCallbacks je nejdůležitější obsah struktury předané ve druhém parametru. Ideální je nejprve tuto strukturu vynulovat, tj. naplnit všechny ukazatele na hodnotu NULL (to by mělo být přenositelné na různé platformy, nezávisle na velikosti ukazatelů). Posléze se nastaví POUZE potřebné ukazatele, které musí ukazovat na funkce s předepsanými typy parametrů (to je kontrolováno při překladu):

/*
 * Registrace vsech callback funkci.
 */
jvmtiError register_all_callback_functions(jvmtiEnv *jvmti)
{
    jvmtiEventCallbacks callbacks;
    jvmtiError error_code;
 
    memset(&callbacks, 0, sizeof(callbacks));
 
    /* JVMTI_EVENT_VM_INIT */
    callbacks.VMInit = &callback_on_vm_init;
 
    /* JVMTI_EVENT_VM_DEATH */
    callbacks.VMDeath = &callback_on_vm_death;
 
    /* JVMTI_EVENT_METHOD_ENTRY */
    callbacks.MethodEntry = &callback_on_method_entry;
 
    error_code = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, (jint)sizeof(callbacks));
    check_jvmti_error(jvmti, error_code, "Cannot set JVM TI callbacks");
    return error_code;
}

Hlavičky všech tří registrovaných callback funkcí musejí vypadat následovně:

/*
 * Callback funkce zavolana pri inicializaci virtualniho stroje.
 */
static void JNICALL callback_on_vm_init(jvmtiEnv *jvmti_env, JNIEnv* env, jthread thread);
/*
 * Callback funkce zavolana pri ukonceni cinnosti virtualniho stroje.
 */
static void JNICALL callback_on_vm_death(jvmtiEnv *jvmti_env, JNIEnv* env);
/*
 * Callback funkce zavolana pri zavolani metody.
 */
static void JNICALL callback_on_method_entry(jvmtiEnv *jvmti, JNIEnv* env,
        jthread thread, jmethodID method);

7. Nastavení režimů notifikace

Ovšem samotná registrace callback funkcí nestačí k tomu, aby je virtuální stroj Javy skutečně zavolal v případě výskytu nějaké události. Aby vše fungovalo k naší spokojenosti, musíme provést ještě dva kroky. Prvním krokem je nastavení požadovaných schopností agenta, kde budeme vyžadovat, aby agent mohl dostávat informace o volání javovských metod. Pokud by daný virtuální stroj Javy tuto funkcionalitu neposkytoval (to se teoreticky může stát), dojde při nastavování požadovaných schopností agenta přes funkci AddCapabilities k chybě, kterou lze detekovat:

/*
 * Nastaveni pozadovanych schopnosti agenta.
 */
jvmtiError set_capabilities(jvmtiEnv *jvmti)
{
    jvmtiCapabilities capabilities;
    jvmtiError error_code;
 
    memset(&capabilities, 0, sizeof(jvmtiCapabilities));
 
    /* jedna specialni schopnost agenta */
    capabilities.can_generate_method_entry_events = 1;
 
    error_code = (*jvmti)->AddCapabilities(jvmti, &capabilities);
    check_jvmti_error(jvmti, error_code, "Unable to get necessary JVMTI capabilities.");
    return error_code;
}

Ve druhém kroku je nutné zachytávání událostí povolit. Tvůrci rozhraní JVM TI totiž počítali s tím, že agent bude zakazovat či naopak povolovat zachytávání událostí v závislosti na nějaké externí konfiguraci (dejme tomu přechod debuggeru z krokování do režimu normálního běhu). Povolení či zákaz zachytávání událostí se provádí přes metodu SetEventNotificationMode, které se předává číselná hodnota události:

/*
 * Nastaveni jedne udalosti, pro nez se ma zavolat callback funkce.
 */
jvmtiError set_event_notification_mode(jvmtiEnv *jvmti, int event)
{
    jvmtiError error_code;
 
    error_code = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, event, (jthread)NULL);
    check_jvmti_error(jvmti, error_code, "Cannot set event notification");
    return error_code;
}
/*
 * Nastaveni udalosti, pro nez se maji zavolat callback funkce.
 */
jvmtiError set_event_notification_modes(jvmtiEnv *jvmti)
{
    jvmtiError error_code;
 
    /* Potrebujeme zachytavat udalost inicializace virtualniho stroje. */
    if ((error_code = set_event_notification_mode(jvmti, JVMTI_EVENT_VM_INIT)) != JNI_OK)
    {
        return error_code;
    }
 
    /* Potrebujeme zachytavat udalost ukonceni prace virtualniho stroje. */
    if ((error_code = set_event_notification_mode(jvmti, JVMTI_EVENT_VM_DEATH)) != JNI_OK)
    {
        return error_code;
    }
 
    /* Potrebujeme zachytavat udalost pri vstupu do metody. */
    if ((error_code = set_event_notification_mode(jvmti, JVMTI_EVENT_METHOD_ENTRY)) != JNI_OK)
    {
        return error_code;
    }
 
    return error_code;
}

A to je v podstatě vše – postačuje jen doplnit několik pomocných funkcí pro kontrolu chyb a můžeme vytvořit třetí demonstrační příklad, který zaregistruje tři callback funkce vyvolávané při třech typech událostí.

8. Třetí demonstrační příklad: agent s dvojicí zaregistrovaných callback funkcí

Zdrojový kód třetího demonstračního příkladu je již sice poměrně dlouhý, ale v následující části tohoto seriálu na něm budeme moci postavit další funkcionalitu:

widgety

/*
 * JVM TI agent s trojici registrovanych callback funkci,
 * ktery dokaze registrovat vstup do metod.
 */
 
#include <stdlib.h>
#include <string.h>
 
/* Nutno nastavit cestu pres volbu -Icesta_k_jvm */
#include <jvmti.h>
 
#define AGENT_NAME "Agent3:"
 
/*
 * Vypis zpravy na standardni vystup.
 */
#define MSG(message) puts(AGENT_NAME " " message)
 
/*
 * Vypis kodu chyby a chybove zpravy.
 */
static void print_jvmti_error(jvmtiEnv *jvmti, jvmtiError error_code, const char *str)
{
    char *error_code_str = NULL;
    const char *msg_str = str == NULL ? "" : str;
    char *msg_err = NULL;
 
    (*jvmti)->GetErrorName(jvmti, error_code, &error_code_str);
    msg_err = error_code_str == NULL ? "Unknown" : error_code_str;
    printf(AGENT_NAME " ERROR: JVMTI: %d(%s): %s\n", error_code, msg_err, msg_str);
}
 
/*
 * Pokud je predany navratovy kod chybovym kodem, vypise se hlaseni.
 */
static void check_jvmti_error(jvmtiEnv *jvmti, jvmtiError error_code, const char *str)
{
    if ( error_code != JVMTI_ERROR_NONE )
    {
        print_jvmti_error(jvmti, error_code, str);
    }
}
 
/*
 * Nastaveni pozadovanych schopnosti agenta.
 */
jvmtiError set_capabilities(jvmtiEnv *jvmti)
{
    jvmtiCapabilities capabilities;
    jvmtiError error_code;
 
    memset(&capabilities, 0, sizeof(jvmtiCapabilities));
 
    /* jedna specialni schopnost agenta */
    capabilities.can_generate_method_entry_events = 1;
 
    error_code = (*jvmti)->AddCapabilities(jvmti, &capabilities);
    check_jvmti_error(jvmti, error_code, "Unable to get necessary JVMTI capabilities.");
    return error_code;
}
 
/*
 * Callback funkce zavolana pri inicializaci virtualniho stroje.
 */
static void JNICALL callback_on_vm_init(jvmtiEnv *jvmti_env, JNIEnv* env, jthread thread)
{
    MSG("Got VM init event");
}
 
/*
 * Callback funkce zavolana pri ukonceni cinnosti virtualniho stroje.
 */
static void JNICALL callback_on_vm_death(jvmtiEnv *jvmti_env, JNIEnv* env)
{
    MSG("Got VM Death event");
}
 
/*
 * Callback funkce zavolana pri zavolani metody.
 */
static void JNICALL callback_on_method_entry(jvmtiEnv *jvmti, JNIEnv* env,
        jthread thread, jmethodID method)
{
    MSG("Got Method Entry event");
}
 
/*
 * Registrace vsech callback funkci.
 */
jvmtiError register_all_callback_functions(jvmtiEnv *jvmti)
{
    jvmtiEventCallbacks callbacks;
    jvmtiError error_code;
 
    memset(&callbacks, 0, sizeof(callbacks));
 
    /* JVMTI_EVENT_VM_INIT */
    callbacks.VMInit = &callback_on_vm_init;
 
    /* JVMTI_EVENT_VM_DEATH */
    callbacks.VMDeath = &callback_on_vm_death;
 
    /* JVMTI_EVENT_METHOD_ENTRY */
    callbacks.MethodEntry = &callback_on_method_entry;
 
    error_code = (*jvmti)->SetEventCallbacks(jvmti, &callbacks, (jint)sizeof(callbacks));
    check_jvmti_error(jvmti, error_code, "Cannot set JVM TI callbacks");
    return error_code;
}
 
/*
 * Nastaveni jedne udalosti, pro nez se ma zavolat callback funkce.
 */
jvmtiError set_event_notification_mode(jvmtiEnv *jvmti, int event)
{
    jvmtiError error_code;
 
    error_code = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, event, (jthread)NULL);
    check_jvmti_error(jvmti, error_code, "Cannot set event notification");
    return error_code;
}
 
/*
 * Nastaveni udalosti, pro nez se maji zavolat callback funkce.
 */
jvmtiError set_event_notification_modes(jvmtiEnv *jvmti)
{
    jvmtiError error_code;
 
    /* Potrebujeme zachytavat udalost inicializace virtualniho stroje. */
    if ((error_code = set_event_notification_mode(jvmti, JVMTI_EVENT_VM_INIT)) != JNI_OK)
    {
        return error_code;
    }
 
    /* Potrebujeme zachytavat udalost ukonceni prace virtualniho stroje. */
    if ((error_code = set_event_notification_mode(jvmti, JVMTI_EVENT_VM_DEATH)) != JNI_OK)
    {
        return error_code;
    }
 
    /* Potrebujeme zachytavat udalost pri vstupu do metody. */
    if ((error_code = set_event_notification_mode(jvmti, JVMTI_EVENT_METHOD_ENTRY)) != JNI_OK)
    {
        return error_code;
    }
 
    return error_code;
}
 
/*
 * Funkce zavolana ve chvili nacitani agenta do JVM.
 */
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved)
{
    jvmtiEnv *jvmti = NULL;
    jint result;
    jvmtiError error_code;
 
    MSG("Agent_OnLoad");
    result = (*jvm)->GetEnv(jvm, (void **) &jvmti, JVMTI_VERSION_1_0);
    if (result != JNI_OK || jvmti == NULL)
    {
        printf("ERROR: Unable to access JVMTI Version 1 (0x%x),"
                " is your J2SE a 1.5 or newer version? JNIEnv's GetEnv() returned %d\n",
                JVMTI_VERSION_1, (int)result);
        return result;
    }
    MSG("JVM TI version is correct");
 
    /* nastaveni pozadovanych schopnosti agenta */
    if ((error_code = set_capabilities(jvmti)) != JNI_OK)
    {
        return error_code;
    }
 
    /* registrace vsech (dvou) callback funkci */
    if ((error_code = register_all_callback_functions(jvmti)) != JNI_OK)
    {
        return error_code;
    }
 
    /* nastaveni udalosti, pro nez se maji zavolat callback funkce */
    if ((error_code = set_event_notification_modes(jvmti)) != JNI_OK)
    {
        return error_code;
    }
 
    return JNI_OK;
}
 
/*
 * Funkce zavolana ve chvili odstranovani agenta z JVM.
 */
JNIEXPORT void JNICALL Agent_OnUnload(JavaVM *vm)
{
    MSG("Agent_OnUnload");
}
 
/*
 * finito
 */

Pokud agenta vyzkoušíme, měl by se na standardním výstupu objevit přibližně následující text:

java -agentpath:./libagent3.so Test
 
Agent3: Agent_OnLoad
Agent3: JVM TI version is correct
Agent3: Got VM init event
Agent3: Got Method Entry event
Agent3: Got Method Entry event
Agent3: Got Method Entry event
 
... několik set! stejných typů událostí
 
Agent3: Got Method Entry event
Agent3: Got Method Entry event
Agent3: Got Method Entry event
Agent3: Got VM Death event
Agent3: Agent_OnUnload

V další části seriálu bude příklad vylepšen, například o výpis signatury a třídy volaných metod. Taktéž si ukážeme, jak lze registrovat callback funkce pro další typy událostí, takže se agent stane mnohem praktičtější.

9. Odkazy na Internetu

  1. The JVM Tool Interface (JVM TI): How VM Agents Work
    http://www.oracle.com/technet­work/articles/javase/jvm-ti-141370.html
  2. JVM Tool Interface Version 1.2
    http://docs.oracle.com/ja­vase/7/docs/platform/jvmti/jvmti­.html
  3. Creating a Debugging and Profiling Agent with JVMTI
    http://www.oracle.com/technet­work/articles/javase/jvmti-136367.html
  4. JVM TI (Wikipedia)
    http://en.wikipedia.org/wiki/JVM_TI
  5. IBM JVMTI extensions
    http://publib.boulder.ibm­.com/infocenter/realtime/v2r0/in­dex.jsp?topic=%2Fcom.ibm.sof­trt.doc%2Fdiag%2Ftools%2Fjvmti_ex­tensions.html
  6. ClojureScript One: Index
    http://clojurescriptone.com/in­dex.html
  7. ClojureScript One: Documentation
    http://clojurescriptone.com/do­cumentation.html
  8. ClojureScript One: Wiki
    https://github.com/brento­nashworth/one/wiki
  9. ClojureScript: Quick Start
    https://github.com/clojure/clo­jurescript/wiki/Quick-Start
  10. Getting Started with ClojureScript (and FW/1)
    http://corfield.org/entry/getting-started-with-clojurescript-and-fw-1
  11. First ClojureScript experiences: using Raphaël
    http://maurits.wordpress.com/2012/02/13/fir­st-clojurescript-experiences-using-raphael/
  12. Raphaël-JavaScript Library
    http://raphaeljs.com/
  13. A detailed installation Guide for VimClojure 2.2
    http://www.duenas.at/new_ho­mepage/vimclojure
  14. VimClojure : A filetype, syntax and indent plugin for Clojure
    http://www.vim.org/scripts/scrip­t.php?script_id=2501
  15. Nailgun server
    http://www.martiansoftware­.com/nailgun/background.html
  16. SLIME (Wikipedia)
    http://en.wikipedia.org/wiki/SLIME
  17. slime.vim
    http://s3.amazonaws.com/mps/slime.vim
  18. Textový editor Vim jako IDE: 1. část
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide/
  19. Textový editor Vim jako IDE: 2. část
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-2-cast/
  20. Textový editor Vim jako IDE: 3. část (omni completion)
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-3-cast/
  21. Textový editor Vim jako IDE: 4. část
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-4-cast/
  22. Textový editor Vim jako IDE: 5. část
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-5-cast/
  23. Textový editor Vim jako IDE: 6. část – Vim Script
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-6-cast-vim-script/
  24. Textový editor Vim jako IDE: 7. část – Vim Script
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-7-cast-vim-script/
  25. Textový editor Vim jako IDE: 8. část
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-8-cast/
  26. Textový editor Vim jako IDE: 9. část – pluginy Netrw a snipMate
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-9-cast-pluginy-netrw-a-snipmate/
  27. Textový editor Vim jako IDE: 10. část – různé tipy a triky
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-10-cast-ruzne-tipy-a-triky/
  28. Přenos textů mezi Vimem a dalšími aplikacemi
    http://www.root.cz/clanky/prenos-textu-mezi-vimem-a-dalsimi-aplikacemi/
  29. Textový editor Vim: konfigurace pravítka a stavového řádku
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-12-cast-konfigurace-pravitka-a-stavoveho-radku/
  30. Textový editor Vim: automatické formátování textů
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-14-cast-automaticke-formatovani-textu/
  31. Textový editor Vim: automatické formátování textů
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-automaticke-formatovani-textu-dokonceni/
  32. Textový editor Vim: editace XML a HTML
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-15-cast-editace-xml-a-html/
  33. Textový editor Vim: kooperace mezi Vimem a skripty
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-16-cast-kooperace-mezi-vimem-a-skriptovacimi-jazyky/
  34. Textový editor Vim: kooperace mezi Vimem a jazykem Perl
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-17-cast-kooperace-mezi-vimem-a-jazykem-perl/
  35. Textový editor Vim: konfigurace a překlad Vimu
    http://www.root.cz/clanky/textovy-editor-vim-jako-ide-18-cast-konfigurace-a-preklad-vimu/
  36. Counterclockwise
    http://code.google.com/p/cou­nterclockwise/
  37. Clojure IDEs – The Grand Tour
    http://www.bestinclass.dk/in­dex.clj/2010/03/clojure-ides-the-grand-tour-getting-started.html
  38. Light Table – a new IDE concept
    http://www.chris-granger.com/2012/04/12/light-table---a-new-ide-concept/
  39. SICP (The Structure and Interpretation of Computer Programs)
    http://mitpress.mit.edu/sicp/
  40. Pure function
    http://en.wikipedia.org/wi­ki/Pure_function
  41. Funkcionální programování
    http://cs.wikipedia.org/wi­ki/Funkcionální_programová­ní
  42. Čistě funkcionální (datové struktury, jazyky, programování)
    http://cs.wikipedia.org/wi­ki/Čistě_funkcionální
  43. Clojure Macro Tutorial (Part I, Getting the Compiler to Write Your Code For You)
    http://www.learningclojure­.com/2010/09/clojure-macro-tutorial-part-i-getting.html
  44. Clojure Macro Tutorial (Part II: The Compiler Strikes Back)
    http://www.learningclojure­.com/2010/09/clojure-macro-tutorial-part-ii-compiler.html
  45. Clojure Macro Tutorial (Part III: Syntax Quote)
    http://www.learningclojure­.com/2010/09/clojure-macro-tutorial-part-ii-syntax.html
  46. Tech behind Tech: Clojure Macros Simplified
    http://techbehindtech.com/2010/09/28/clo­jure-macros-simplified/
  47. Fatvat – Exploring functional programming: Clojure Macros
    http://www.fatvat.co.uk/2009/02/clo­jure-macros.html
  48. Eulerovo číslo
    http://cs.wikipedia.org/wi­ki/Eulerovo_číslo
  49. List comprehension
    http://en.wikipedia.org/wi­ki/List_comprehension
  50. List Comprehensions in Clojure
    http://asymmetrical-view.com/2008/11/18/list-comprehensions-in-clojure.html
  51. Clojure Programming Concepts: List Comprehension
    http://en.wikibooks.org/wi­ki/Clojure_Programming/Con­cepts#List_Comprehension
  52. Clojure core API: for macro
    http://clojure.github.com/clo­jure/clojure.core-api.html#clojure.core/for
  53. cirrus machina – The Clojure for macro
    http://www.cirrusmachina.com/blog/com­ment/the-clojure-for-macro/
  54. Clojure.org: Clojure home page
    http://clojure.org/downloads
  55. Clojure.org: Vars and the Global Environment
    http://clojure.org/Vars
  56. Clojure.org: Refs and Transactions
    http://clojure.org/Refs
  57. Clojure.org: Atoms
    http://clojure.org/Atoms
  58. Clojure.org: Agents as Asynchronous Actions
    http://clojure.org/agents
  59. A Couple of Clojure Agent Examples
    http://lethain.com/a-couple-of-clojure-agent-examples/
  60. Clojure – Functional Programming for the JVM
    http://java.ociweb.com/mar­k/clojure/article.html
  61. Clojure quick reference
    http://faustus.webatu.com/clj-quick-ref.html
  62. 4Clojure
    http://www.4clojure.com/
  63. ClojureDoc
    http://clojuredocs.org/
  64. Clojure (Wikipedia EN)
    http://en.wikipedia.org/wiki/Clojure
  65. Clojure (Wikipedia CS)
    http://cs.wikipedia.org/wiki/Clojure
  66. Riastradh's Lisp Style Rules
    http://mumble.net/~campbe­ll/scheme/style.txt
  67. Dynamic Languages Strike Back
    http://steve-yegge.blogspot.cz/2008/05/dynamic-languages-strike-back.html
  68. Scripting: Higher Level Programming for the 21st Century
    http://www.tcl.tk/doc/scripting.html
  69. Java Virtual Machine Support for Non-Java Languages
    http://docs.oracle.com/ja­vase/7/docs/technotes/gui­des/vm/multiple-language-support.html
  70. New JDK 7 Feature: Support for Dynamically Typed Languages in the Java Virtual Machine
    http://java.sun.com/develo­per/technicalArticles/Dyn­TypeLang/
  71. JSR 223: Scripting for the JavaTM Platform
    http://jcp.org/en/jsr/detail?id=223
  72. JSR 292: Supporting Dynamically Typed Languages on the JavaTM Platform
    http://jcp.org/en/jsr/detail?id=292
  73. Java 7: A complete invokedynamic example
    http://niklasschlimm.blog­spot.com/2012/02/java-7-complete-invokedynamic-example.html
  74. InvokeDynamic: Actually Useful?
    http://blog.headius.com/2007/01/in­vokedynamic-actually-useful.html
  75. A First Taste of InvokeDynamic
    http://blog.headius.com/2008/09/first-taste-of-invokedynamic.html
  76. Java 6 try/finally compilation without jsr/ret
    http://cliffhacks.blogspot­.com/2008/02/java-6-tryfinally-compilation-without.html
  77. An empirical study of Java bytecode programs
    http://www.mendeley.com/research/an-empirical-study-of-java-bytecode-programs/
  78. Java quick guide: JVM Instruction Set (tabulka všech instrukcí JVM)
    http://www.mobilefish.com/tu­torials/java/java_quickgu­ide_jvm_instruction_set.html
  79. The JVM Instruction Set
    http://mpdeboer.home.xs4a­ll.nl/scriptie/node14.html
  80. Control Flow in the Java Virtual Machine
    http://www.artima.com/under­thehood/flowP.html
  81. Root.cz: Využití komprimovaných ukazatelů na objekty v JVM
    http://www.root.cz/clanky/vyuziti-komprimovanych-ukazatelu-na-objekty-v-nbsp-jvm/
  82. Root.cz: JamVM aneb alternativa k HotSpotu nejenom pro embedded zařízení a chytré telefony
    http://www.root.cz/clanky/jamvm-aneb-alternativa-k-hotspotu-nejenom-pro-embedded-zarizeni-tablety-a-chytre-telefony/
  83. The JavaTM Virtual Machine Specification, Second Edition
    http://java.sun.com/docs/bo­oks/jvms/second_edition/html/VMSpec­TOC.doc.html
  84. The class File Format
    http://java.sun.com/docs/bo­oks/jvms/second_edition/html/Clas­sFile.doc.html
  85. javap – The Java Class File Disassembler
    http://docs.oracle.com/ja­vase/1.4.2/docs/tooldocs/win­dows/javap.html
  86. javap-java-1.6.0-openjdk(1) – Linux man page
    http://linux.die.net/man/1/javap-java-1.6.0-openjdk
  87. Using javap
    http://www.idevelopment.in­fo/data/Programming/java/mis­cellaneous_java/Using_javap­.html
  88. Examine class files with the javap command
    http://www.techrepublic.com/ar­ticle/examine-class-files-with-the-javap-command/5815354
  89. BCEL Home page
    http://commons.apache.org/bcel/
  90. BCEL Manual
    http://commons.apache.org/bcel/ma­nual.html
  91. Byte Code Engineering Library (Wikipedia)
    http://en.wikipedia.org/wiki/BCEL
  92. Java programming dynamics, Part 7: Bytecode engineering with BCEL
    http://www.ibm.com/develo­perworks/java/library/j-dyn0414/
  93. Bytecode Engineering
    http://book.chinaunix.net/spe­cial/ebook/Core_Java2_Volu­me2AF/0131118269/ch13lev1sec6­.html
  94. BCEL Tutorial
    http://www.smfsupport.com/sup­port/java/bcel-tutorial!/
  95. ASM Home page
    http://asm.ow2.org/
  96. Seznam nástrojů využívajících projekt ASM
    http://asm.ow2.org/users.html
  97. ObjectWeb ASM (Wikipedia)
    http://en.wikipedia.org/wi­ki/ObjectWeb_ASM
  98. Java Bytecode BCEL vs ASM
    http://james.onegoodcooki­e.com/2005/10/26/java-bytecode-bcel-vs-asm/
  99. Bytecode Outline plugin for Eclipse (screenshoty + info)
    http://asm.ow2.org/eclipse/index.html
  100. aspectj (Eclipse)
    http://www.eclipse.org/aspectj/
  101. Aspect-oriented programming (Wikipedia)
    http://en.wikipedia.org/wi­ki/Aspect_oriented_program­ming
  102. AspectJ (Wikipedia)
    http://en.wikipedia.org/wiki/AspectJ
  103. EMMA: a free Java code coverage tool
    http://emma.sourceforge.net/
  104. Cobertura
    http://cobertura.sourceforge.net/
  105. FindBugs
    http://findbugs.sourceforge.net/
  106. GNU Classpath
    www.gnu.org/s/classpath/
  107. Java VMs Compared
    http://bugblogger.com/java-vms-compared-160/
  108. JSRs: Java Specification Requests – JSR 223: Scripting for the Java Platform
    http://www.jcp.org/en/jsr/de­tail?id=223
  109. Scripting for the Java Platform
    http://java.sun.com/develo­per/technicalArticles/J2SE/Des­ktop/scripting/
  110. Scripting for the Java Platform (Wikipedia)
    http://en.wikipedia.org/wi­ki/Scripting_for_the_Java_Plat­form
  111. Java Community Process
    http://en.wikipedia.org/wi­ki/Java_Specification_Requ­est
  112. Java HotSpot VM Options
    http://www.oracle.com/technet­work/java/javase/tech/vmop­tions-jsp-140102.html
  113. Great Computer Language Shootout
    http://c2.com/cgi/wiki?Gre­atComputerLanguageShootout
  114. Java performance
    http://en.wikipedia.org/wi­ki/Java_performance
  115. Trying the prototype
    http://mail.openjdk.java.net/pi­permail/lambda-dev/2010-August/002179.html
  116. Better closures (for Java)
    http://blogs.sun.com/jrose/en­try/better_closures
  117. Lambdas in Java: An In-Depth Analysis
    http://www.infoq.com/articles/lambdas-java-analysis
  118. Class ReflectiveOperationException
    http://download.java.net/jdk7/doc­s/api/java/lang/Reflective­OperationException.html
  119. Scala Programming Language
    http://www.scala-lang.org/
  120. Run Scala in Apache Tomcat in 10 minutes
    http://www.softwaresecret­weapons.com/jspwiki/run-scala-in-apache-tomcat-in-10-minutes
  121. Fast Web Development With Scala
    http://chasethedevil.blog­spot.cz/2007/09/fast-web-development-with-scala.html
  122. Top five scripting languages on the JVM
    http://www.infoworld.com/d/developer-world/top-five-scripting-languages-the-jvm-855
  123. Proposal: Indexing access syntax for Lists and Maps
    http://mail.openjdk.java.net/pi­permail/coin-dev/2009-March/001108.html
  124. Proposal: Elvis and Other Null-Safe Operators
    http://mail.openjdk.java.net/pi­permail/coin-dev/2009-March/000047.html
  125. Java 7 : Oracle pushes a first version of closures
    http://www.baptiste-wicht.com/2010/05/oracle-pushes-a-first-version-of-closures/
  126. Groovy: An agile dynamic language for the Java Platform
    http://groovy.codehaus.org/Operators
  127. Better Strategies for Null Handling in Java
    http://www.slideshare.net/Step­han.Schmidt/better-strategies-for-null-handling-in-java
  128. Control Flow in the Java Virtual Machine
    http://www.artima.com/under­thehood/flowP.html
  129. Java Virtual Machine
    http://en.wikipedia.org/wi­ki/Java_virtual_machine
  130. ==, .equals(), compareTo(), and compare()
    http://leepoint.net/notes-java/data/expressions/22com­pareobjects.html
  131. New JDK7 features
    http://openjdk.java.net/pro­jects/jdk7/features/
  132. Project Coin: Bringing it to a Close(able)
    http://blogs.sun.com/darcy/en­try/project_coin_bring_clo­se
  133. CloseableFinder source code
    http://blogs.sun.com/darcy/re­source/ProjectCoin/Closea­bleFinder.java
  134. Joe Darcy blog about JDK
    http://blogs.sun.com/darcy
  135. Java 7 – more dynamics
    http://www.baptiste-wicht.com/2010/04/java-7-more-dynamics/
  136. New JDK 7 Feature: Support for Dynamically Typed Languages in the Java Virtual Machine
    http://java.sun.com/develo­per/technicalArticles/Dyn­TypeLang/index.html
Našli jste v článku chybu?
Lupa.cz: Blíží se konec Wi-Fi sítí bez hesla?

Blíží se konec Wi-Fi sítí bez hesla?

Lupa.cz: Proč jsou firemní počítače pomalé?

Proč jsou firemní počítače pomalé?

Lupa.cz: Další Češi si nechali vložit do těla čip

Další Češi si nechali vložit do těla čip

Měšec.cz: TEST: Vyzkoušeli jsme pražské taxikáře

TEST: Vyzkoušeli jsme pražské taxikáře

120na80.cz: Pálení žáhy: která jídla ne a co nás uzdraví?

Pálení žáhy: která jídla ne a co nás uzdraví?

DigiZone.cz: Ginx TV: pořad o počítačových hráčích

Ginx TV: pořad o počítačových hráčích

DigiZone.cz: Parlamentní listy: kde končí PR...

Parlamentní listy: kde končí PR...

Vitalia.cz: 5 chyb, které děláme při skladování potravin

5 chyb, které děláme při skladování potravin

DigiZone.cz: Digi Slovakia zařazuje stanice SPI

Digi Slovakia zařazuje stanice SPI

DigiZone.cz: Technisat připravuje trojici DAB

Technisat připravuje trojici DAB

Podnikatel.cz: Instalatér, malíř a elektrikář. "Vymřou"?

Instalatér, malíř a elektrikář. "Vymřou"?

Vitalia.cz: Pryč se zastaralým stravováním ve školách

Pryč se zastaralým stravováním ve školách

Vitalia.cz: Test dětských svačinek: Tyhle ne!

Test dětských svačinek: Tyhle ne!

Vitalia.cz: Fyzioterapeutka: Chůze naboso? Rozhodně ano!

Fyzioterapeutka: Chůze naboso? Rozhodně ano!

Podnikatel.cz: Letáky? Lidi zuří, ale ony stále fungují

Letáky? Lidi zuří, ale ony stále fungují

Vitalia.cz: Jak Ondra o astma přišel

Jak Ondra o astma přišel

DigiZone.cz: Funbox 4K v DVB-T2 má ostrý provoz

Funbox 4K v DVB-T2 má ostrý provoz

DigiZone.cz: Světový pohár v přímém přenosu na ČT

Světový pohár v přímém přenosu na ČT

Podnikatel.cz: Babišovi se nedá věřit, stěžovali si hospodští

Babišovi se nedá věřit, stěžovali si hospodští

DigiZone.cz: DVB-T2 ověřeno: seznam TV zveřejněn

DVB-T2 ověřeno: seznam TV zveřejněn