JVM Tool Interface
Remarks#
JVM TM Tool Interface
Version 1.2
https://docs.oracle.com/javase/7/docs/platform/jvmti/jvmti.html
Iterate over objects reachable from object (Heap 1.0)
#include <vector>
#include <string>
#include "agent_util.hpp"
//this file can be found in Java SE Development Kit 8u101 Demos and Samples
//see https://download.oracle.com/otn-pub/java/jdk/8u101-b13-demos/jdk-8u101-windows-x64-demos.zip
//jdk1.8.0_101.zip!\demo\jvmti\versionCheck\src\agent_util.h
/*
* Struct used for jvmti->SetTag(object, <pointer to tag>);
* https://docs.oracle.com/javase/7/docs/platform/jvmti/jvmti.html#SetTag
*
*/
typedef struct Tag
{
jlong referrer_tag;
jlong size;
char* classSignature;
jint hashCode;
} Tag;
/*
* Utility function: jlong -> Tag*
*/
static Tag* pointerToTag(jlong tag_ptr)
{
if (tag_ptr == 0)
{
return new Tag();
}
return (Tag*)(ptrdiff_t)(void*)tag_ptr;
}
/*
* Utility function: Tag* -> jlong
*/
static jlong tagToPointer(Tag* tag)
{
return (jlong)(ptrdiff_t)(void*)tag;
}
/*
* Heap 1.0 Callback
* https://docs.oracle.com/javase/7/docs/platform/jvmti/jvmti.html#jvmtiObjectReferenceCallback
*/
static jvmtiIterationControl JNICALL heabObjectReferencesCallback(
jvmtiObjectReferenceKind reference_kind,
jlong class_tag,
jlong size,
jlong* tag_ptr,
jlong referrer_tag,
jint referrer_index,
void* user_data)
{
//iterate only over reference field
if (reference_kind != JVMTI_HEAP_REFERENCE_FIELD)
{
return JVMTI_ITERATION_IGNORE;
}
auto tag_ptr_list = (std::vector<jlong>*)(ptrdiff_t)(void*)user_data;
//create and assign tag
auto t = pointerToTag(*tag_ptr);
t->referrer_tag = referrer_tag;
t->size = size;
*tag_ptr = tagToPointer(t);
//collect tag
(*tag_ptr_list).push_back(*tag_ptr);
return JVMTI_ITERATION_CONTINUE;
}
/*
* Main function for demonstration of Iterate Over Objects Reachable From Object
* https://docs.oracle.com/javase/7/docs/platform/jvmti/jvmti.html#IterateOverObjectsReachableFromObject
*
*/
void iterateOverObjectHeapReferences(jvmtiEnv* jvmti, JNIEnv* env, jobject object)
{
std::vector<jlong> tag_ptr_list;
auto t = new Tag();
jvmti->SetTag(object, tagToPointer(t));
tag_ptr_list.push_back(tagToPointer(t));
stdout_message("tag list size before call callback: %d\n", tag_ptr_list.size());
/*
* Call Callback for every reachable object reference
* see https://docs.oracle.com/javase/7/docs/platform/jvmti/jvmti.html#IterateOverObjectsReachableFromObject
*/
jvmti->IterateOverObjectsReachableFromObject(object, &heabObjectReferencesCallback, (void*)&tag_ptr_list);
stdout_message("tag list size after call callback: %d\n", tag_ptr_list.size());
if (tag_ptr_list.size() > 0)
{
jint found_count = 0;
jlong* tags = &tag_ptr_list[0];
jobject* found_objects;
jlong* found_tags;
/*
* collect all tagged object (via *tag_ptr = pointer to tag )
* see https://docs.oracle.com/javase/7/docs/platform/jvmti/jvmti.html#GetObjectsWithTags
*/
jvmti->GetObjectsWithTags(tag_ptr_list.size(), tags, &found_count, &found_objects, &found_tags);
stdout_message("found %d objects\n", found_count);
for (auto i = 0; i < found_count; ++i)
{
jobject found_object = found_objects[i];
char* classSignature;
jclass found_object_class = env->GetObjectClass(found_object);
/*
* Get string representation of found_object_class
* see https://docs.oracle.com/javase/7/docs/platform/jvmti/jvmti.html#GetClassSignature
*/
jvmti->GetClassSignature(found_object_class, &classSignature, nullptr);
jint hashCode;
/*
* Getting hash code for found_object
* see https://docs.oracle.com/javase/7/docs/platform/jvmti/jvmti.html#GetObjectHashCode
*/
jvmti->GetObjectHashCode(found_object, &hashCode);
//save all it in Tag
Tag* t = pointerToTag(found_tags[i]);
t->classSignature = classSignature;
t->hashCode = hashCode;
}
//print all saved information
for (auto i = 0; i < found_count; ++i)
{
auto t = pointerToTag(found_tags[i]);
auto rt = pointerToTag(t->referrer_tag);
if (t->referrer_tag != 0)
{
stdout_message("referrer object %s#%d --> object %s#%d (size: %2d)\n",
rt->classSignature, rt->hashCode, t->classSignature, t->hashCode, t->size);
}
}
}
}
Get JVMTI environment
Inside Agent_OnLoad method:
jvmtiEnv* jvmti;
/* Get JVMTI environment */
vm->GetEnv(reinterpret_cast<void **>(&jvmti), JVMTI_VERSION);
Example of initialization inside of Agent_OnLoad method
/* Callback for JVMTI_EVENT_VM_INIT */
static void JNICALL vm_init(jvmtiEnv* jvmti, JNIEnv* env, jthread thread)
{
jint runtime_version;
jvmti->GetVersionNumber(&runtime_version);
stdout_message("JVMTI Version: %d\n", runtime_verision);
}
/* Agent_OnLoad() is called first, we prepare for a VM_INIT event here. */
JNIEXPORT jint JNICALL
Agent_OnLoad(JavaVM* vm, char* options, void* reserved)
{
jint rc;
jvmtiEventCallbacks callbacks;
jvmtiCapabilities capabilities;
jvmtiEnv* jvmti;
/* Get JVMTI environment */
rc = vm->GetEnv(reinterpret_cast<void **>(&jvmti), JVMTI_VERSION);
if (rc != JNI_OK)
{
return -1;
}
/* Immediately after getting the jvmtiEnv* we need to ask for the
* capabilities this agent will need.
*/
jvmti->GetCapabilities(&capabilities);
capabilities.can_tag_objects = 1;
jvmti->AddCapabilities(&capabilities);
/* Set callbacks and enable event notifications */
memset(&callbacks, 0, sizeof(callbacks));
callbacks.VMInit = &vm_init;
jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks));
jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, nullptr);
return JNI_OK;
}