Obsah
2. První demonstrační příklad – kostra jednoduchého JVM TI 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í
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:
/* * 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
- The JVM Tool Interface (JVM TI): How VM Agents Work
http://www.oracle.com/technetwork/articles/javase/jvm-ti-141370.html - JVM Tool Interface Version 1.2
http://docs.oracle.com/javase/7/docs/platform/jvmti/jvmti.html - Creating a Debugging and Profiling Agent with JVMTI
http://www.oracle.com/technetwork/articles/javase/jvmti-136367.html - JVM TI (Wikipedia)
http://en.wikipedia.org/wiki/JVM_TI - IBM JVMTI extensions
http://publib.boulder.ibm.com/infocenter/realtime/v2r0/index.jsp?topic=%2Fcom.ibm.softrt.doc%2Fdiag%2Ftools%2Fjvmti_extensions.html - ClojureScript One: Index
http://clojurescriptone.com/index.html - ClojureScript One: Documentation
http://clojurescriptone.com/documentation.html - ClojureScript One: Wiki
https://github.com/brentonashworth/one/wiki - ClojureScript: Quick Start
https://github.com/clojure/clojurescript/wiki/Quick-Start - Getting Started with ClojureScript (and FW/1)
http://corfield.org/entry/getting-started-with-clojurescript-and-fw-1 - First ClojureScript experiences: using Raphaël
http://maurits.wordpress.com/2012/02/13/first-clojurescript-experiences-using-raphael/ - Raphaël-JavaScript Library
http://raphaeljs.com/ - A detailed installation Guide for VimClojure 2.2
http://www.duenas.at/new_homepage/vimclojure - VimClojure : A filetype, syntax and indent plugin for Clojure
http://www.vim.org/scripts/script.php?script_id=2501 - Nailgun server
http://www.martiansoftware.com/nailgun/background.html - SLIME (Wikipedia)
http://en.wikipedia.org/wiki/SLIME - slime.vim
http://s3.amazonaws.com/mps/slime.vim - Textový editor Vim jako IDE: 1. část
http://www.root.cz/clanky/textovy-editor-vim-jako-ide/ - Textový editor Vim jako IDE: 2. část
http://www.root.cz/clanky/textovy-editor-vim-jako-ide-2-cast/ - Textový editor Vim jako IDE: 3. část (omni completion)
http://www.root.cz/clanky/textovy-editor-vim-jako-ide-3-cast/ - Textový editor Vim jako IDE: 4. část
http://www.root.cz/clanky/textovy-editor-vim-jako-ide-4-cast/ - Textový editor Vim jako IDE: 5. část
http://www.root.cz/clanky/textovy-editor-vim-jako-ide-5-cast/ - Textový editor Vim jako IDE: 6. část – Vim Script
http://www.root.cz/clanky/textovy-editor-vim-jako-ide-6-cast-vim-script/ - Textový editor Vim jako IDE: 7. část – Vim Script
http://www.root.cz/clanky/textovy-editor-vim-jako-ide-7-cast-vim-script/ - Textový editor Vim jako IDE: 8. část
http://www.root.cz/clanky/textovy-editor-vim-jako-ide-8-cast/ - 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/ - 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/ - Přenos textů mezi Vimem a dalšími aplikacemi
http://www.root.cz/clanky/prenos-textu-mezi-vimem-a-dalsimi-aplikacemi/ - 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/ - Textový editor Vim: automatické formátování textů
http://www.root.cz/clanky/textovy-editor-vim-jako-ide-14-cast-automaticke-formatovani-textu/ - Textový editor Vim: automatické formátování textů
http://www.root.cz/clanky/textovy-editor-vim-jako-ide-automaticke-formatovani-textu-dokonceni/ - Textový editor Vim: editace XML a HTML
http://www.root.cz/clanky/textovy-editor-vim-jako-ide-15-cast-editace-xml-a-html/ - 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/ - 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/ - Textový editor Vim: konfigurace a překlad Vimu
http://www.root.cz/clanky/textovy-editor-vim-jako-ide-18-cast-konfigurace-a-preklad-vimu/ - Counterclockwise
http://code.google.com/p/counterclockwise/ - Clojure IDEs – The Grand Tour
http://www.bestinclass.dk/index.clj/2010/03/clojure-ides-the-grand-tour-getting-started.html - Light Table – a new IDE concept
http://www.chris-granger.com/2012/04/12/light-table---a-new-ide-concept/ - SICP (The Structure and Interpretation of Computer Programs)
http://mitpress.mit.edu/sicp/ - Pure function
http://en.wikipedia.org/wiki/Pure_function - Funkcionální programování
http://cs.wikipedia.org/wiki/Funkcionální_programování - Čistě funkcionální (datové struktury, jazyky, programování)
http://cs.wikipedia.org/wiki/Čistě_funkcionální - 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 - Clojure Macro Tutorial (Part II: The Compiler Strikes Back)
http://www.learningclojure.com/2010/09/clojure-macro-tutorial-part-ii-compiler.html - Clojure Macro Tutorial (Part III: Syntax Quote)
http://www.learningclojure.com/2010/09/clojure-macro-tutorial-part-ii-syntax.html - Tech behind Tech: Clojure Macros Simplified
http://techbehindtech.com/2010/09/28/clojure-macros-simplified/ - Fatvat – Exploring functional programming: Clojure Macros
http://www.fatvat.co.uk/2009/02/clojure-macros.html - Eulerovo číslo
http://cs.wikipedia.org/wiki/Eulerovo_číslo - List comprehension
http://en.wikipedia.org/wiki/List_comprehension - List Comprehensions in Clojure
http://asymmetrical-view.com/2008/11/18/list-comprehensions-in-clojure.html - Clojure Programming Concepts: List Comprehension
http://en.wikibooks.org/wiki/Clojure_Programming/Concepts#List_Comprehension - Clojure core API: for macro
http://clojure.github.com/clojure/clojure.core-api.html#clojure.core/for - cirrus machina – The Clojure for macro
http://www.cirrusmachina.com/blog/comment/the-clojure-for-macro/ - Clojure.org: Clojure home page
http://clojure.org/downloads - Clojure.org: Vars and the Global Environment
http://clojure.org/Vars - Clojure.org: Refs and Transactions
http://clojure.org/Refs - Clojure.org: Atoms
http://clojure.org/Atoms - Clojure.org: Agents as Asynchronous Actions
http://clojure.org/agents - A Couple of Clojure Agent Examples
http://lethain.com/a-couple-of-clojure-agent-examples/ - Clojure – Functional Programming for the JVM
http://java.ociweb.com/mark/clojure/article.html - Clojure quick reference
http://faustus.webatu.com/clj-quick-ref.html - 4Clojure
http://www.4clojure.com/ - ClojureDoc
http://clojuredocs.org/ - Clojure (Wikipedia EN)
http://en.wikipedia.org/wiki/Clojure - Clojure (Wikipedia CS)
http://cs.wikipedia.org/wiki/Clojure - Riastradh's Lisp Style Rules
http://mumble.net/~campbell/scheme/style.txt - Dynamic Languages Strike Back
http://steve-yegge.blogspot.cz/2008/05/dynamic-languages-strike-back.html - Scripting: Higher Level Programming for the 21st Century
http://www.tcl.tk/doc/scripting.html - Java Virtual Machine Support for Non-Java Languages
http://docs.oracle.com/javase/7/docs/technotes/guides/vm/multiple-language-support.html - New JDK 7 Feature: Support for Dynamically Typed Languages in the Java Virtual Machine
http://java.sun.com/developer/technicalArticles/DynTypeLang/ - JSR 223: Scripting for the JavaTM Platform
http://jcp.org/en/jsr/detail?id=223 - JSR 292: Supporting Dynamically Typed Languages on the JavaTM Platform
http://jcp.org/en/jsr/detail?id=292 - Java 7: A complete invokedynamic example
http://niklasschlimm.blogspot.com/2012/02/java-7-complete-invokedynamic-example.html - InvokeDynamic: Actually Useful?
http://blog.headius.com/2007/01/invokedynamic-actually-useful.html - A First Taste of InvokeDynamic
http://blog.headius.com/2008/09/first-taste-of-invokedynamic.html - Java 6 try/finally compilation without jsr/ret
http://cliffhacks.blogspot.com/2008/02/java-6-tryfinally-compilation-without.html - An empirical study of Java bytecode programs
http://www.mendeley.com/research/an-empirical-study-of-java-bytecode-programs/ - Java quick guide: JVM Instruction Set (tabulka všech instrukcí JVM)
http://www.mobilefish.com/tutorials/java/java_quickguide_jvm_instruction_set.html - The JVM Instruction Set
http://mpdeboer.home.xs4all.nl/scriptie/node14.html - Control Flow in the Java Virtual Machine
http://www.artima.com/underthehood/flowP.html - Root.cz: Využití komprimovaných ukazatelů na objekty v JVM
http://www.root.cz/clanky/vyuziti-komprimovanych-ukazatelu-na-objekty-v-nbsp-jvm/ - 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/ - The JavaTM Virtual Machine Specification, Second Edition
http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html - The class File Format
http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html - javap – The Java Class File Disassembler
http://docs.oracle.com/javase/1.4.2/docs/tooldocs/windows/javap.html - javap-java-1.6.0-openjdk(1) – Linux man page
http://linux.die.net/man/1/javap-java-1.6.0-openjdk - Using javap
http://www.idevelopment.info/data/Programming/java/miscellaneous_java/Using_javap.html - Examine class files with the javap command
http://www.techrepublic.com/article/examine-class-files-with-the-javap-command/5815354 - BCEL Home page
http://commons.apache.org/bcel/ - BCEL Manual
http://commons.apache.org/bcel/manual.html - Byte Code Engineering Library (Wikipedia)
http://en.wikipedia.org/wiki/BCEL - Java programming dynamics, Part 7: Bytecode engineering with BCEL
http://www.ibm.com/developerworks/java/library/j-dyn0414/ - Bytecode Engineering
http://book.chinaunix.net/special/ebook/Core_Java2_Volume2AF/0131118269/ch13lev1sec6.html - BCEL Tutorial
http://www.smfsupport.com/support/java/bcel-tutorial!/ - ASM Home page
http://asm.ow2.org/ - Seznam nástrojů využívajících projekt ASM
http://asm.ow2.org/users.html - ObjectWeb ASM (Wikipedia)
http://en.wikipedia.org/wiki/ObjectWeb_ASM - Java Bytecode BCEL vs ASM
http://james.onegoodcookie.com/2005/10/26/java-bytecode-bcel-vs-asm/ - Bytecode Outline plugin for Eclipse (screenshoty + info)
http://asm.ow2.org/eclipse/index.html - aspectj (Eclipse)
http://www.eclipse.org/aspectj/ - Aspect-oriented programming (Wikipedia)
http://en.wikipedia.org/wiki/Aspect_oriented_programming - AspectJ (Wikipedia)
http://en.wikipedia.org/wiki/AspectJ - EMMA: a free Java code coverage tool
http://emma.sourceforge.net/ - Cobertura
http://cobertura.sourceforge.net/ - FindBugs
http://findbugs.sourceforge.net/ - GNU Classpath
www.gnu.org/s/classpath/ - Java VMs Compared
http://bugblogger.com/java-vms-compared-160/ - JSRs: Java Specification Requests – JSR 223: Scripting for the Java Platform
http://www.jcp.org/en/jsr/detail?id=223 - Scripting for the Java Platform
http://java.sun.com/developer/technicalArticles/J2SE/Desktop/scripting/ - Scripting for the Java Platform (Wikipedia)
http://en.wikipedia.org/wiki/Scripting_for_the_Java_Platform - Java Community Process
http://en.wikipedia.org/wiki/Java_Specification_Request - Java HotSpot VM Options
http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html - Great Computer Language Shootout
http://c2.com/cgi/wiki?GreatComputerLanguageShootout - Java performance
http://en.wikipedia.org/wiki/Java_performance - Trying the prototype
http://mail.openjdk.java.net/pipermail/lambda-dev/2010-August/002179.html - Better closures (for Java)
http://blogs.sun.com/jrose/entry/better_closures - Lambdas in Java: An In-Depth Analysis
http://www.infoq.com/articles/lambdas-java-analysis - Class ReflectiveOperationException
http://download.java.net/jdk7/docs/api/java/lang/ReflectiveOperationException.html - Scala Programming Language
http://www.scala-lang.org/ - Run Scala in Apache Tomcat in 10 minutes
http://www.softwaresecretweapons.com/jspwiki/run-scala-in-apache-tomcat-in-10-minutes - Fast Web Development With Scala
http://chasethedevil.blogspot.cz/2007/09/fast-web-development-with-scala.html - Top five scripting languages on the JVM
http://www.infoworld.com/d/developer-world/top-five-scripting-languages-the-jvm-855 - Proposal: Indexing access syntax for Lists and Maps
http://mail.openjdk.java.net/pipermail/coin-dev/2009-March/001108.html - Proposal: Elvis and Other Null-Safe Operators
http://mail.openjdk.java.net/pipermail/coin-dev/2009-March/000047.html - Java 7 : Oracle pushes a first version of closures
http://www.baptiste-wicht.com/2010/05/oracle-pushes-a-first-version-of-closures/ - Groovy: An agile dynamic language for the Java Platform
http://groovy.codehaus.org/Operators - Better Strategies for Null Handling in Java
http://www.slideshare.net/Stephan.Schmidt/better-strategies-for-null-handling-in-java - Control Flow in the Java Virtual Machine
http://www.artima.com/underthehood/flowP.html - Java Virtual Machine
http://en.wikipedia.org/wiki/Java_virtual_machine - ==, .equals(), compareTo(), and compare()
http://leepoint.net/notes-java/data/expressions/22compareobjects.html - New JDK7 features
http://openjdk.java.net/projects/jdk7/features/ - Project Coin: Bringing it to a Close(able)
http://blogs.sun.com/darcy/entry/project_coin_bring_close - CloseableFinder source code
http://blogs.sun.com/darcy/resource/ProjectCoin/CloseableFinder.java - Joe Darcy blog about JDK
http://blogs.sun.com/darcy - Java 7 – more dynamics
http://www.baptiste-wicht.com/2010/04/java-7-more-dynamics/ - New JDK 7 Feature: Support for Dynamically Typed Languages in the Java Virtual Machine
http://java.sun.com/developer/technicalArticles/DynTypeLang/index.html