shvm-agent: README.md and comments

parent 4b4e7667
Pipeline #4501 passed with stages
in 3 minutes and 47 seconds
......@@ -782,10 +782,10 @@
</target>
<!-- Run all tests -->
<target name="test" depends="test-disl,test-shvm,test-comopound"></target>
<target name="test" depends="test-disl,test-shvm,test-compound"></target>
<!-- Run compound tests -->
<target name="test-comopound" depends="build,build-test" description="Runs all tests or a selected (-Dtest.name=...) test suite.">
<target name="test-compound" depends="build,build-test" description="Runs all tests or a selected (-Dtest.name=...) test suite.">
<!--
If test.name is set to a name of a test suite, only include the test suite
......
# SHVM native agent library
The purpose of this agent is to handle the invocation of REDispatch methods in
java code and send the analysis requests to the server.
## How is this done?
Firstly, the agent initializes itself. That means parsing the options,
requesting and veryfying the JVMTI capabilities, setting necessary callbacks
estabilishing the connection with the server, initializing of blocking queues,
registering the shared locks within JVM and launch of the sending thread.
Registered callbacks are:
* Class file load event
* Class prepare hook
* Object free hook
* JVM start hook
* JVM init hook
* JVM death hooks
* Thread end hook
Registered locks are:
* Tagging lock
* to buff lock - a.k.a. total ordering
* object free lock
Initialized blocking queues are:
* Utility queue - Holds the buffers that will be used for notifying about new
classes, registering analysis methods etc.
* Empty queue - Holds the empty buffers
* objtag queue - This queue is consumed by object tagging queue
* send queue - consumed by sending thread.
### Class prepare callback
While the JVM loads the classes, we listen for the class prepare callback and
when the `ch.usi.dag.dislre.REDispatch` class is loaded, then we register the
REDispatch methods such as `registerMethod`, `analysisStart` etc. Those are
just the static method that can later be called from
### Class load callback & Tagging
> Disclaimer:
> The terms "Tag" and "net reference" are used interchangeable in this memo.
The class is loaded into JVM, we are gonna send the class including its code
to the analysis server, but first is required to assign the unique identifier
to it. This is called **tagging**.
The tag is 64 bit long and has following structure:
```
| 1 : 1b | 2 : 1b | 3 : 23b | 4 : 40b |
```
1. SPEC bit - This bit indicates, whether some additional data were sent to the
server
2. ClassInstance bit - This bit indicates, whether the object is class
instance, or just a class. According to implementation, 1 stands for class
here and 0 for object instance.
3. ClassID - Unique identifier of the class
4. ObjectID - Unique identifier of the object
The tags are managed by JVM, but they has to be created manually when the
class is first seen, which moment is right in that callback. While tagging,
the following two cases are distinguished:
1. Tagging a class
2. Tagging a class instance (object)
While tagging a class we need to obtain the net reference of the class loader
and also the net reference of the superclass, thus tagging can be initiated
recursively, although that shouldn't be a problem, since we probably had seen
the classloader and even the superclass loading before and they will have their
tags already. However, sending class to the observing VM is performed in this
phase.
When tagging a object, we simply obtain the tag of class and create the tag
using this and a global counter.
......@@ -16,16 +16,27 @@ typedef struct {
} buffer;
// ******************* Buffer routines *******************
void buffer_alloc(buffer * b) {
/*
* buffer_alloc()
*
* Allocate the space for buffer. To free the space see buffer_free()
*/
void
buffer_alloc(buffer * b)
{
b->buff = (unsigned char *) malloc(INIT_BUFF_SIZE);
b->capacity = INIT_BUFF_SIZE;
b->occupied = 0;
}
void buffer_free(buffer * b) {
/*
* buffer_free()
*
* Free the allocated space for the buffer
*/
void
buffer_free(buffer * b)
{
free(b->buff);
b->buff = NULL;
b->capacity = 0;
......@@ -69,15 +80,39 @@ void buffer_fill_at_pos(buffer * b, size_t pos, const void * data,
memcpy(b->buff + pos, data, data_length);
}
void buffer_read(buffer * b, size_t pos, void * data, size_t data_length) {
/*
* buffer_read()
*
* Copy the amount of data specified in data_length from buffer specified by
* b, starting at position pos (number of bytes from the start of the buffer)
* to place specified by parameter data.
*/
void
buffer_read(buffer * b, size_t pos, void * data, size_t data_length)
{
memcpy(data, b->buff + pos, data_length);
}
size_t buffer_filled(buffer * b) {
/*
* buffer_filled(buffer * b)
*
* Returns the number of occupied bytes in given buffer
*/
size_t
buffer_filled(buffer * b)
{
return b->occupied;
}
void buffer_clean(buffer * b) {
/*
* buffer_clean(buffer * b)
*
* Clean the buffer by setting the number of occupied bytes to zero. This will
* not affect the data stored.
*/
void
buffer_clean(buffer * b)
{
b->occupied = 0;
}
......
This diff is collapsed.
......@@ -27,7 +27,7 @@ static volatile jint avail_class_id;
// bit field not used because there is no guarantee of alignment
// TODO rename SPEC
// SPEC flag is used to indicate if some additional data for this object where
// SPEC flag is used to indicate if some additional data for this object were
// transfered to the server
static const uint8_t OBJECT_ID_POS = 0;
......@@ -40,10 +40,22 @@ static const uint64_t CLASS_ID_MASK = 0x3FFFFF;
static const uint64_t CLASS_INSTANCE_MASK = 0x1;
static const uint64_t SPEC_MASK = 0x1;
// get bits from "from" with pattern "bit_mask" lowest bit starting on position
// "low_start" (from 0)
static inline uint64_t get_bits(uint64_t from, uint64_t bit_mask,
uint8_t low_start) {
/*
* get_bits()
*
* get bits from "from" with pattern "bit_mask" lowest bit starting on position
* "low_start" (from 0)
*
* EXAMPLE
* ...
* uint64_t from = 0xC0FFEE;
* uint64_t result = get_bits(from, 0xFF, 16);
* ...
* After this call the value in result will be 0xC0
*/
static inline uint64_t
get_bits(uint64_t from, uint64_t bit_mask, uint8_t low_start)
{
// shift it
uint64_t bits_shifted = from >> low_start;
......@@ -52,10 +64,27 @@ static inline uint64_t get_bits(uint64_t from, uint64_t bit_mask,
return bits_shifted & bit_mask;
}
// set bits "bits" to "to" with pattern "bit_mask" lowest bit starting on
// position "low_start" (from 0)
static inline void set_bits(uint64_t * to, uint64_t bits,
uint64_t bit_mask, uint8_t low_start) {
/*
* set_bits()
*
* set bits "bits" to "to" with pattern "bit_mask" lowest bit starting on
* position "low_start" (from 0).
*
* WARNING
* Note that this function can only be used to set bits from zeros
* to ones and not in the other direction.
*
* EXAMPLE
* ...
* uint64_t to = 0L;
* set_bits(&to, TRUE, 0x1, 63)
* ...
* After this call, the value of to will be 0x8000_0000_0000_0000
*
*/
static inline void
set_bits(uint64_t * to, uint64_t bits, uint64_t bit_mask, uint8_t low_start)
{
// mask it
uint64_t bits_len = bits & bit_mask;
......@@ -65,7 +94,14 @@ static inline void set_bits(uint64_t * to, uint64_t bits,
*to |= bits_pos;
}
static inline jlong net_ref_get_object_id(jlong net_ref) {
/*
* net_ref_get_object_id()
*
* Return the object id part from net_ref value
*/
static inline jlong
net_ref_get_object_id(jlong net_ref)
{
return get_bits(net_ref, OBJECT_ID_MASK, OBJECT_ID_POS);
}
......@@ -101,8 +137,15 @@ static inline void net_ref_set_spec(jlong * net_ref, unsigned char spec) {
// TODO comment
// only retrieves object tag data
jlong get_tag(jvmtiEnv * jvmti_env, jobject obj) {
/*
* get_tag()
*
* Only retrieves object tag data from JVM. Returns zero, when no tag
* is registered within JVM.
*/
jlong
get_tag(jvmtiEnv * jvmti_env, jobject obj)
{
jlong net_ref;
jvmtiError error = (*jvmti_env)->GetTag(jvmti_env, obj, &net_ref);
......@@ -115,8 +158,15 @@ jlong get_tag(jvmtiEnv * jvmti_env, jobject obj) {
jlong get_net_reference(JNIEnv * jni_env, jvmtiEnv * jvmti_env,
buffer * new_obj_buff, jobject obj);
// !!! returned local reference should be freed
static jclass _get_class_for_object(JNIEnv * jni_env, jobject obj) {
/*
* _get_class_for_object()
*
* For an object, return its class.
* !!! returned local reference should be freed
*/
static jclass
_get_class_for_object(JNIEnv * jni_env, jobject obj)
{
return (*jni_env)->GetObjectClass(jni_env, obj);
}
......@@ -134,9 +184,16 @@ static int _object_is_class(jvmtiEnv * jvmti_env, jobject obj) {
return TRUE;
}
// does not increment any counter - just sets the values
static jlong _set_net_reference(jvmtiEnv * jvmti_env, jobject obj,
jlong object_id, jint class_id, unsigned char spec, unsigned char cbit) {
/*
* _set_net_reference()
*
* Encode parameters into the net reference.
* does not increment any counter - just sets the values
*/
static jlong
_set_net_reference(jvmtiEnv * jvmti_env, jobject obj,
jlong object_id, jint class_id, unsigned char spec, unsigned char cbit)
{
jlong net_ref = 0;
......@@ -145,6 +202,8 @@ static jlong _set_net_reference(jvmtiEnv * jvmti_env, jobject obj,
net_ref_set_spec(&net_ref, spec);
net_ref_set_class_instance(&net_ref, cbit);
// This piece of code is already implemented as the function
// update_net_reference()
jvmtiError error = (*jvmti_env)->SetTag(jvmti_env, obj, net_ref);
check_jvmti_error(jvmti_env, error, "Cannot set object tag");
......@@ -177,8 +236,20 @@ static void _pack_class_info(buffer * buff, jlong class_net_ref,
}
static jlong _set_net_reference_for_class(JNIEnv * jni_env,
jvmtiEnv * jvmti_env, buffer * buff, jclass klass) {
/*
* _set_net_reference_for_class()
*
* Create new net reference for given class, also resolve class loader and
* superclass net references and then pack them as MSG_CLASS_INFO into
* the buffer. The resolution of net_reference of the superclass and
* the classloader is done via get_net_reference, thus this function might get
* called recursively for all superclasses up to the root of the inheritance
* hierarchy.
*/
static jlong
_set_net_reference_for_class(JNIEnv * jni_env,
jvmtiEnv * jvmti_env, buffer * buff, jclass klass)
{
// manage references
// http://docs.oracle.com/javase/6/docs/platform/jvmti/jvmti.html#refs
......@@ -267,8 +338,17 @@ static jint _get_class_id_for_object(JNIEnv * jni_env, jvmtiEnv * jvmti_env,
return class_id;
}
static jlong _set_net_reference_for_object(JNIEnv * jni_env,
jvmtiEnv * jvmti_env, buffer * buff, jobject obj) {
/*
* _set_net_reference_for_object()
*
* Determine the class of the object, get the class id, and return the whole
* net reference for object instance. Also increment the global object_id that
* will be set to the next object.
*/
static jlong
_set_net_reference_for_object(JNIEnv * jni_env,
jvmtiEnv * jvmti_env, buffer * buff, jobject obj)
{
// resolve class id
jint class_id = _get_class_id_for_object(jni_env, jvmti_env, buff, obj);
......@@ -283,12 +363,23 @@ static jlong _set_net_reference_for_object(JNIEnv * jni_env,
return net_ref;
}
// retrieves net_reference - performs tagging if necessary
// can be used for any object - even classes
// !!! invocation of this method should be protected by lock until the reference
// is queued for sending
jlong get_net_reference(JNIEnv * jni_env, jvmtiEnv * jvmti_env,
buffer * new_obj_buff, jobject obj) {
/*
* get_net_reference()
*
* retrieves net_reference - performs tagging if necessary
* can be used for any object - even classes
* !!! invocation of this method should be protected by lock until the reference
* is queued for sending
*
* The purpose of new_obj_buffer here is to provide a place, where
* the MSG_CLASS_INFO messages about the new objects, such as superclasses
* or classloaders will be buffered (see the comment to
* _set_net_reference_for_class()).
*/
jlong
get_net_reference(JNIEnv * jni_env, jvmtiEnv * jvmti_env,
buffer * new_obj_buff, jobject obj)
{
if(obj == NULL) { // net reference for NULL is 0
return 0;
......@@ -316,10 +407,15 @@ jlong get_net_reference(JNIEnv * jni_env, jvmtiEnv * jvmti_env,
return net_ref;
}
// !!! invocation of this method should be protected by lock until the reference
// is queued for sending
void update_net_reference(jvmtiEnv * jvmti_env, jobject obj, jlong net_ref) {
/*
* update_net_reference()
*
* !!! invocation of this method should be protected by lock until the reference
* is queued for sending
*/
void
update_net_reference(jvmtiEnv * jvmti_env, jobject obj, jlong net_ref)
{
jvmtiError error = (*jvmti_env)->SetTag(jvmti_env, obj, net_ref);
check_jvmti_error(jvmti_env, error, "Cannot set object tag");
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment