...
 
Commits (72)
# Backup files
*~
# Generated dependency files
src-disl-agent/*.d
src-disl-agent/linux-*
src-disl-agent/windows-*
*.d
# Compiled shared libraries
*.so
......@@ -17,12 +18,15 @@ examples/**/*.jar
# Logs
*.log
# Output directories
src-disl-agent/linux-*
src-disl-agent/windows-*
# Anything in the output
output/*
# Libraries
# Runtime libraries
lib/*/*.jar
# Fallout from method uid generator
methodid.txt
......@@ -58,7 +58,12 @@ endif
# Source and object files needed to create the library
SOURCES = dislreagent.c
SOURCES = common.c jvmtiutil.c config.c \
shared/bytebuffer.c shared/bufferpack.c \
shared/blockingqueue.c \
shared/threadlocal.c shared/messagetype.c \
tagger.c sender.c dislreagent.c pbmanager.c redispatcher.c netref.c \
globalbuffer.c tlocalbuffer.c freehandler.c
HEADERS = $(wildcard *.h)
GENSRCS =
......@@ -92,8 +97,8 @@ endif
CFLAGS += -I$(INCLUDE_DIR) -I$(INCLUDE_DIR)/$(JDK_TARGET)
# Source code options
CFLAGS += -W -Wall -Wextra -Wno-unused-parameter
#CFLAGS += -std=gnu99 -W -Wall -Wextra -Wno-unused-parameter
#CFLAGS += -W -Wall -Wextra -Wno-unused-parameter
CFLAGS += -std=gnu99 -W -Wall -Wextra -Wno-unused-parameter
# Fix-up quirks before first target
-include Makefile.quirks
......
#ifndef _BLOCKINGQUEUE_H
#define _BLOCKINGQUEUE_H
#include <string.h>
#include <pthread.h>
#include <jvmti.h>
#include <jni.h>
#include "jvmtihelper.h"
typedef struct {
// array of elements
char * qarray;
size_t qa_size;
size_t qa_element_size;
size_t first;
size_t occupied;
pthread_mutex_t mutex;
pthread_cond_t cond;
jvmtiEnv * jvmti;
} blocking_queue;
// ** Monitor helper functions **
static void _bq_monitor_enter(blocking_queue * bq) {
pthread_mutex_lock(&(bq->mutex));
}
static void _bq_monitor_exit(blocking_queue * bq) {
pthread_mutex_unlock(&(bq->mutex));
}
static void _bq_monitor_wait(blocking_queue * bq) {
pthread_cond_wait(&(bq->cond), &(bq->mutex));
}
static void _bq_monitor_notify_all(blocking_queue * bq) {
pthread_cond_broadcast(&(bq->cond));
}
// ** Blocking queue functions **
void bq_create(jvmtiEnv *jvmti, blocking_queue * bq, size_t queue_capacity,
size_t queue_element_size) {
check_std_error((bq == NULL), TRUE, "Invalid blocking queue structure");
bq->qarray = malloc(queue_capacity * queue_element_size);
bq->qa_size = queue_capacity;
bq->qa_element_size = queue_element_size;
bq->first = 0;
bq->occupied = 0;
// create lock and cond
int pci = pthread_cond_init(&(bq->cond), NULL);
check_std_error((pci != 0), TRUE, "Cannot create pthread condition");
int pmi = pthread_mutex_init(&(bq->mutex), NULL);
check_std_error((pmi != 0), TRUE, "Cannot create pthread mutex");
bq->jvmti = jvmti;
}
void bq_term (blocking_queue * bq) {
_bq_monitor_enter(bq); {
// delete array
free(bq->qarray);
bq->qarray = NULL;
}
_bq_monitor_exit(bq);
// destroy lock and cond
pthread_mutex_destroy(&(bq->mutex));
pthread_cond_destroy(&(bq->cond));
}
void bq_push(blocking_queue * bq, void * data) {
_bq_monitor_enter(bq); {
// wait for some empty space
while(bq->occupied == bq->qa_size) {
_bq_monitor_wait(bq);
}
// add data
size_t last = (bq->first + bq->occupied) % bq->qa_size;
size_t last_pos = last * bq->qa_element_size;
memcpy(&((bq->qarray)[last_pos]), data, bq->qa_element_size);
++(bq->occupied);
// notify
_bq_monitor_notify_all(bq);
}
_bq_monitor_exit(bq);
}
void bq_pop(blocking_queue * bq, void * empty) {
_bq_monitor_enter(bq); {
// wait for some item
while(bq->occupied == 0) {
_bq_monitor_wait(bq);
}
// get the data
size_t first_pos = bq->first * bq->qa_element_size;
memcpy(empty, &((bq->qarray)[first_pos]), bq->qa_element_size);
// insert 0 - better problem detection
memset(&((bq->qarray)[first_pos]), 0, bq->qa_element_size);
bq->first = (bq->first + 1) % bq->qa_size;
--(bq->occupied);
// notify
_bq_monitor_notify_all(bq);
}
_bq_monitor_exit(bq);
}
size_t bq_length(blocking_queue * bq) {
size_t length = 0;
_bq_monitor_enter(bq); {
length = bq->occupied;
}
_bq_monitor_exit(bq);
return length;
}
#endif /* _BLOCKINGQUEUE_H */
#ifndef _BUFFER_H
#define _BUFFER_H
#include <stdlib.h>
#include <string.h>
#include "jvmtihelper.h"
// initial buffer size
static const size_t INIT_BUFF_SIZE = 512;
typedef struct {
unsigned char * buff;
size_t occupied;
size_t capacity;
} buffer;
// ******************* Buffer routines *******************
/*
* 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;
}
/*
* buffer_free()
*
* Free the allocated space for the buffer
*/
void
buffer_free(buffer * b)
{
free(b->buff);
b->buff = NULL;
b->capacity = 0;
b->occupied = 0;
}
void buffer_fill(buffer * b, const void * data, size_t data_length) {
// not enough free space - extend buffer
if(b->capacity - b->occupied < data_length) {
unsigned char * old_buff = b->buff;
// alloc as much as needed to be able to insert data
size_t new_capacity = 2 * b->capacity;
while(new_capacity - b->occupied < data_length) {
new_capacity *= 2;
}
b->buff = (unsigned char *) malloc(new_capacity);
b->capacity = new_capacity;
memcpy(b->buff, old_buff, b->occupied);
free(old_buff);
}
memcpy(b->buff + b->occupied, data, data_length);
b->occupied += data_length;
}
// the space has to be already filled with data - no extensions
void buffer_fill_at_pos(buffer * b, size_t pos, const void * data,
size_t data_length) {
// space is not filled already - error
if(b->occupied < pos + data_length) {
check_error(TRUE, "Filling buffer at non-occupied position.");
}
memcpy(b->buff + pos, data, 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);
}
/*
* buffer_filled(buffer * b)
*
* Returns the number of occupied bytes in given buffer
*/
size_t
buffer_filled(buffer * b)
{
return b->occupied;
}
/*
* 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;
}
#endif /* _BUFFER_H */
#ifndef _BUFFPACK_H
#define _BUFFPACK_H
#if defined (__APPLE__) && defined (__MACH__)
#include <machine/endian.h>
#if BYTE_ORDER == BIG_ENDIAN
#define htobe64(x) (x)
#else // BYTE_ORDER != BIG_ENDIAN
#define htobe64(x) __DARWIN_OSSwapInt64((x))
#endif
#else // !(__APPLE__ && __MACH__)
#include <endian.h>
#endif
// Disabled check to make it compile under OS X with clang/LLVM.
//#ifndef __STDC_IEC_559__
//#error "Requires IEEE 754 floating point!"
//#endif
#include "jvmtihelper.h"
#include "buffer.h"
// interpret bytes differently
union float_jint {
float f;
jint i;
};
// interpret bytes differently
union double_jlong {
double d;
jlong l;
};
void pack_boolean(buffer * buff, jboolean to_send) {
buffer_fill(buff, &to_send, sizeof(jboolean));
}
void pack_byte(buffer * buff, jbyte to_send) {
buffer_fill(buff, &to_send, sizeof(jbyte));
}
void pack_char(buffer * buff, jchar to_send) {
jchar nts = htons(to_send);
buffer_fill(buff, &nts, sizeof(jchar));
}
void pack_short(buffer * buff, jshort to_send) {
jshort nts = htons(to_send);
buffer_fill(buff, &nts, sizeof(jshort));
}
void pack_int(buffer * buff, jint to_send) {
jint nts = htonl(to_send);
buffer_fill(buff, &nts, sizeof(jint));
}
void pack_long(buffer * buff, jlong to_send) {
jlong nts = htobe64(to_send);
buffer_fill(buff, &nts, sizeof(jlong));
}
void pack_float(buffer * buff, jfloat to_send) {
// macro ensures that the formating of the float is correct
// so make "int" from it and send it
union float_jint convert;
convert.f = to_send;
pack_int(buff, convert.i);
}
void pack_double(buffer * buff, jdouble to_send) {
// macro ensures that the formating of the double is correct
// so make "long" from it and send it
union double_jlong convert;
convert.d = to_send;
pack_long(buff, convert.l);
}
void pack_string_utf8(buffer * buff, const void * string_utf8,
uint16_t size_in_bytes) {
// send length first
uint16_t nsize = htons(size_in_bytes);
buffer_fill(buff, &nsize, sizeof(uint16_t));
buffer_fill(buff, string_utf8, size_in_bytes);
}
void pack_bytes(buffer * buff, const void * data, jint size) {
buffer_fill(buff, data, size);
}
#endif /* _BUFFPACK_H */
#ifndef _COMM_H
#define _COMM_H
#include <sys/types.h>
#include <sys/socket.h>
#include "jvmtihelper.h"
// sends data over network
void send_data(int sockfd, const void * data, int data_len) {
int sent = 0;
while (sent != data_len) {
int res = send(sockfd, ((unsigned char *)data) + sent,
(data_len - sent), 0);
check_std_error(res, -1, "Error while sending data to server");
sent += res;
}
}
// receives data from network
void rcv_data(int sockfd, void * data, int data_len) {
int received = 0;
while (received != data_len) {
int res = recv(sockfd, ((unsigned char *)data) + received,
(data_len - received), 0);
check_std_error(res, -1, "Error while receiving data from server");
received += res;
}
}
#endif /* _COMM_H */
#include "common.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <strings.h>
//
/**
* Returns the index of the given value in the given array of values.
* Returns -1 if the value could not be found among the allowed values.
*/
int
find_value_index (
const char * restrict strval, const char * restrict values [], int nvals
) {
for (int i = 0; i < nvals; i++) {
if (strcasecmp (values [i], strval) == 0) {
return i;
}
}
return -1;
}
//
/**
* Reports an error and terminates the program. This function implements the
* slow path of check_error(). It prints the given error message and exits with
* an error code indicating a generic error.
*/
void
die_with_error (const char * restrict format, va_list args) {
fprintf (stderr, AGENT_NAME ": error: ");
vfprintf (stderr, format, args);
fprintf (stderr, "\n");
exit (ERROR_GENERIC);
}
/**
* Reports a standard library error and terminates the program. This function
* implements the slow path of check_std_error(). It prints the given error
* message along with the error message provided by the standard library and
* exits with an error code indicating failure in standard library call.
*/
void
die_with_std_error (int errnum, const char * restrict format, va_list args) {
char * cause = strerror (errnum);
fprintf (stderr, AGENT_NAME ": std-error: %s\n", cause);
fprintf (stderr, AGENT_NAME ": ");
vfprintf (stderr, format, args);
fprintf (stderr, "\n");
exit (ERROR_STD);
}
#ifdef MINGW
/**
* Obtains an error message for the given error code.
* Allocates a new string that has to be released by the caller.
*/
static char *
__get_error_message (const DWORD msg_id) {
LPVOID msg_buffer = NULL;
size_t size = FormatMessageA (
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, msg_id, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR) &msg_buffer, 0, NULL
);
if (size != 0 && msg_buffer != NULL) {
char * message = strdup (msg_buffer);
LocalFree (msg_buffer);
return message;
} else {
static const char * msg_format = "unknown error (%d)";
size_t msg_length = strlen (msg_format) + ((sizeof(DWORD) * 8) / 3) + 1;
char * message = (char *) malloc (msg_length);
if (message != NULL) {
snprintf (message, msg_length, msg_format, msg_id);
}
return message;
}
}
/**
* Reports a windows error and terminates the program. This function
* implements the slow path of check_win_error(). It prints the given error
* message along with the error message provided by windows and
* exits with an error code indicating failure in standard library call.
*/
void
die_with_win_error (DWORD errnum, const char * restrict format, va_list args) {
const char * restrict cause = __get_error_message (errnum);
fprintf (stderr, AGENT_NAME ": std-error: %s\n", cause);
if (cause != NULL) {
free (cause);
}
fprintf (stderr, AGENT_NAME ": ");
vfprintf (stderr, format, args);
fprintf (stderr, "\n");
exit (ERROR_STD);
}
#endif /* MINGW */
#ifndef _COMMON_H_
#define _COMMON_H_
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdarg.h>
#include <stdbool.h>
#include <errno.h>
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#ifdef MINGW
#include <winsock2.h>
#include <windows.h>
#endif
/**
* Compiler attributes.
*/
#define PACKED __attribute__ ((__packed__))
#if defined(WHOLE) && __has_attribute(externally_visible)
#define VISIBLE __attribute__ ((externally_visible))
#else
#define VISIBLE
#endif
/**
* Returns the size of an array in array elements.
*/
#define sizeof_array(array) \
(sizeof (array) / sizeof ((array) [0]))
/**
* Returns the size of a structure member.
*/
#define sizeof_member(type, member) \
(sizeof (((type *) 0)->member))
/**
* Prints a debug message to stdout, unless NDEBUG is defined.
*
* @format format string for printf
* @args arguments associated with the format string
*
*/
#ifdef NDEBUG
# define debug(args...) do {} while (0)
#else
# define debug(args...) fprintf (stdout, args); fflush (stdout)
#endif
#define ldebug(args...) { \
debug ("%s:%d: ", __FUNCTION__, __LINE__); \
debug (args); \
}
//
#define warn(args...) { \
fprintf (stderr, AGENT_NAME ": warning: "); \
fprintf (stderr, args); \
}
//
int find_value_index (
const char * restrict strval, const char * restrict values [], int nvals
);
//
#define ERROR_GENERIC 10000
#define ERROR_SERVER 10003
#define ERROR_STD 10002
#define ERROR_JVMTI 10003
#define AGENT_NAME "svm-agent"
//
void die_with_error (const char * format, va_list args);
void die_with_std_error (int errnum, const char * format, va_list args);
/**
* Reports a general error and terminates the program if the provided
* error condition is true.
*/
static inline void
check_error (bool error, const char * format, ...) {
if (error) {
va_list args;
va_start (args, format);
die_with_error (format, args);
va_end (args);
}
}
/**
* Reports a standard library error and terminates the program if the provided
* error condition is true.
*/
static inline void
check_std_error (bool error, const char * format, ...) {
if (error) {
va_list args;
va_start (args, format);
die_with_std_error (errno, format, args);
va_end (args);
}
}
//
#ifdef MINGW
void die_with_win_error (DWORD errnum, const char * message, va_list args);
/**
* Reports a windows error and terminates the program if the provided
* error condition is true.
*/
static inline void
check_win_error (bool error, const char * format, ...) {
if (error) {
va_list args;
va_start (args, format);
die_with_win_error (GetLastError (), format, args);
va_end (args);
}
}
#endif /* MINGW */
#endif /* _COMMON_H_ */
#include "config.h"
#include "debug.h"
#include "jvmtiutil.h"
#include <string.h>
#include <jvmti.h>
//
#define PROP_SHADOW_HOST "svm-agent.shadow.host"
#define CONF_SHADOW_HOST_DEFAULT "localhost"
#define PROP_SHADOW_PORT "svm-agent.shadow.port"
#define CONF_SHADOW_PORT_DEFAULT "11218"
#define PROP_DEBUG "svm-agent.debug"
#define CONF_DEBUG_DEFAULT false
//
struct config {
char * shadow_host;
char * shadow_port;
bool debug;
};
/**
* Agent configuration.
*/
static struct config agent_config;
static void
__configure_from_properties (jvmtiEnv * jvmti, struct config * config) {
//
// Get boolean values from system properties
//
config->debug = jvmti_get_system_property_bool (
jvmti, PROP_DEBUG, CONF_DEBUG_DEFAULT
);
}
static void
__configure_from_options (const char * options, struct config * config) {
//
// Assign default Shadow VM host name and port and bail out
// if there are no agent options.
//
if (options == NULL) {
config->shadow_host = strdup (CONF_SHADOW_HOST_DEFAULT);
config->shadow_port = strdup (CONF_SHADOW_PORT_DEFAULT);
return;
}
//
// Parse the host name and port of the Shadow VM server.
// Look for port specification first, then take the prefix
// before ':' as the host name.
//
char * host_start = strdup (options);
char * port_start = strchr (host_start, ':');
if (port_start != NULL) {
//
// Split the option string at the port delimiter (':')
// using an end-of-string character ('\0') and copy
// the port.
//
port_start [0] = '\0';
port_start++;
config->shadow_port = strdup (port_start);
}
config->shadow_host = strdup (host_start);
}
void
config_init (jvmtiEnv * jvmti, const char * options) {
__configure_from_options (options, &agent_config);
__configure_from_properties (jvmti, &agent_config);
//
// Configuration summary.
//
rdexec {
rdaprefix ("host: %s\n", agent_config.shadow_host);
rdaprefix ("port: %s\n", agent_config.shadow_port);
rdaprefix ("debug: %d\n", agent_config.debug);
}
}
bool
config_debug_is_enabled () {
return agent_config.debug;
}
const char *
config_shadow_host () {
return agent_config.shadow_host;
}
const char *
config_shadow_port () {
return agent_config.shadow_port;
}
#ifndef _CONFIG_H_
#define _CONFIG_H_
#include <stdbool.h>
#include <jvmti.h>
void config_init (jvmtiEnv * jvmti, const char * options);
bool config_debug_is_enabled ();
const char * config_shadow_host ();
const char * config_shadow_port ();
#endif /* _CONFIG_H_ */
#ifndef _DEBUG_H_
#define _DEBUG_H_
#include "common.h"
#include "config.h"
/**
* Prints a debug message to stdout (unless NDEBUG is defined).
*
* @args arguments to pass to fprintf()
*
*/
#ifdef NDEBUG
# define debug(args...) do {} while (0)
#else
# define debug(args...) fprintf (stdout, args); fflush (stdout)
#endif
/**
* Prints a debug message including the function name and
* line number to stdout (unless NDEBUG is defined).
*
* @args arguments to pass to the debug() macro.
*/
#define ldebug(args...) { \
debug ("%s:%d: ", __FUNCTION__, __LINE__); \
debug (args); \
}
/**
* Runtime debuging output macros.
*/
#define rdexec \
if (config_debug_is_enabled ())
#define rdoutput(args...) \
fprintf (stdout, args);
#define __safe_name(name) \
(((name) == NULL) ? "<unknown>" : (name))
#define rdaprefix(args...) \
rdoutput (AGENT_NAME ": "); \
rdoutput (args);
#define rdatprefix(info, args...) \
rdoutput (AGENT_NAME " [%s]: ", __safe_name ((info)->name)); \
rdoutput (args);
#define rdatiprefix(info, args...) \
rdoutput (AGENT_NAME " [%s, %" PRId64 "]: ", __safe_name ((info)->name), (info)->id); \
rdoutput (args);
#define rdaprintf(args...) \
rdexec { \
rdaprefix (args); \
}
#define rdatprintf(info, args...) \
rdexec { \
rdatprefix (info, args); \
}
#define rdatiprintf(info, args...) \
rdexec { \
rdatiprefix (info, args); \
}
#endif /* _DEBUG_H_ */
This diff is collapsed.
#include <jvmti.h>
#ifndef _DISLAGENT_H
#define _DISLAGENT_H
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved);
#ifdef __cplusplus
}
#endif
#endif /* _DISLAGENT_H */
#include "freehandler.h"
#include "shared/bytebuffer.h"
#include "shared/bufferpack.h"
#include "shared/messagetype.h"
#include "pbmanager.h"
#include "sender.h"
#include "jvmtiutil.h"
static jrawMonitorID obj_free_lock;
#define MAX_OBJ_FREE_EVENTS 4096
static process_buffs * obj_free_buff = NULL;
static jint obj_free_event_count = 0;
static size_t obj_free_event_count_pos = 0;
void
fh_init (jvmtiEnv * jvmti)
{
jvmtiError error = (*jvmti)->CreateRawMonitor (jvmti, "obj free", &obj_free_lock);
check_jvmti_error (jvmti, error, "failed to create object free lock");
}
/*
* __object_free()
*
* Handle the object free events. Everything in this hook is done with
* locked obj_free_lock.
* If no buffer is allocated, then get new one from utility queue of buffers,
* put a MSG_OBJECT_FREE code on the beginning of the buffer
*
* If the buffer has more than allowed count of events, is send to the analysis
*
*/
static inline void
__object_free (jlong tag)
{
// allocate new obj free buffer
if (obj_free_buff == NULL) {
// obtain buffer
obj_free_buff = pb_utility_get ();
// reset number of events in the buffer
obj_free_event_count = 0;
// get pointer to the location where count of requests will stored
obj_free_event_count_pos = messager_objfree_header (
obj_free_buff->analysis_buff
);
}
// obtain message buffer
buffer_t * buff = obj_free_buff->analysis_buff;
messager_objfree_item (buff, tag);
// update the number of free events
obj_free_event_count++;
buffer_put_jint (buff, obj_free_event_count, obj_free_event_count_pos);
if (obj_free_event_count >= MAX_OBJ_FREE_EVENTS) {
// NOTE: We can queue buffer to the sending queue. This is because
// object tagging thread is first sending the objects and then
// deallocating the global references. We cannot have here objects
// that weren't sent already
// NOTE2: It is mandatory to submit to the sending queue directly
// because GC (that is generating these events) will block the
// tagging thread. And with not working tagging thread, we can
// run out of buffers.
sender_enqueue (obj_free_buff);
// cleanup
obj_free_buff = NULL;
obj_free_event_count = 0;
obj_free_event_count_pos = 0;
}
}
void
fh_object_free (jvmtiEnv * jvmti, jlong tag)
{
enter_critical_section (jvmti, obj_free_lock);
__object_free (tag);
exit_critical_section (jvmti, obj_free_lock);
}
static inline void
__send_buffer ()
{
if (obj_free_buff != NULL) {
sender_enqueue (obj_free_buff);
obj_free_buff = NULL;
}
}
void
fh_send_buffer (jvmtiEnv * jvmti)
{
// send object free buffer - with lock
enter_critical_section (jvmti, obj_free_lock);
__send_buffer ();
exit_critical_section (jvmti, obj_free_lock);
}
#ifndef _FREEHANDLER_H_
#define _FREEHANDLER_H_
#include <jvmti.h>
//
void fh_init (jvmtiEnv * jvmti);
void fh_object_free (jvmtiEnv * jvmti, jlong tag);
void fh_send_buffer (jvmtiEnv * jvmti);
#endif /* _FREEHANDLER_H_ */
#include "globalbuffer.h"
#include "shared/threadlocal.h"
#include "shared/bufferpack.h"
#include "shared/messagetype.h"
#include "pbmanager.h"
#include "tagger.h"
#include "jvmtiutil.h"
// number of analysis requests in one message
#define ANALYSIS_COUNT 16384
#define TO_BUFFER_COUNT (TO_BUFFER_MAX_ID + 1) // +1 for buffer id 0
static jrawMonitorID to_buff_lock;
static to_buff_struct to_buff_array [TO_BUFFER_COUNT];
void
glbuffer_init (jvmtiEnv * jvmti) {
jvmtiError error = (*jvmti)->CreateRawMonitor (jvmti, "buffids", &to_buff_lock);
check_jvmti_error (jvmti, error, "failed to create global buffer raw monitor");
// initialize total ordering buff array
for (unsigned int i = 0; i < sizeof_array (to_buff_array); i++) {
to_buff_array[i].pb = NULL;
}
}
static void
__to_buff_alloc_buffers (to_buff_struct * tobs, tldata * tld) {
tobs->pb = pb_normal_get (tld->id);
// set owner_id to ordering id
tobs->pb->owner_id = tld->to_buff_id;
tobs->analysis_count = 0;
tobs->analysis_count_pos = messager_analyze_header (
tobs->pb->analysis_buff, tld->to_buff_id
);
}
static inline objtag_rec *
__buffer_ptr_objtag (buffer_t * restrict buffer, const size_t position) {
// Buffer should a command buffer containing object tagging records.
return (objtag_rec *) buffer_block_at (buffer, position, sizeof (objtag_rec));
}
static void
__shift_tag_positions (buffer_t * buffer, const size_t shift) {
//
// Shift tag positions in the data buffer in all object tagging
// requests in the given buffer. The buffer is assumed to be a
// command buffer containing only object tagging requests.
//
const size_t len = buffer_length (buffer);
for (size_t pos = 0; pos < len; pos += sizeof (objtag_rec)) {
objtag_rec * ot_record = __buffer_ptr_objtag (buffer, pos);
ot_record->buff_pos += shift;
}
}
/**
* Schedules the payload in the total-order buffer for tagging (and eventually
* for sending to the Shadow VM) and invalidates the enqueued total-order buffer.
*/
static inline void
__enqueue_and_invalidate_payload (to_buff_struct * tobs) {
tagger_enqueue (tobs->pb);
tobs->pb = NULL;
}
/**
* Commits local buffers to buffers totally ordered buffers.
*/
void
glbuffer_commit (jvmtiEnv * jvmti) {
tldata * tld = tld_get ();
enter_critical_section (jvmti, to_buff_lock);
{
//
// Get a total-order buffer for this thread and allocate
// new analysis/command buffers if necessary.
//
to_buff_struct * tobs = &(to_buff_array [tld->to_buff_id]);
if (tobs->pb == NULL) {
__to_buff_alloc_buffers (tobs, tld);
}
//
// Object tagging records in the local command buffer have object
// tag positions relative to the local analysis buffer. Before
// copying the local buffers to the total-order buffer, adjust
// these positions so that they are relative to the total-order.
//
size_t to_offset = buffer_position (tobs->pb->analysis_buff);
__shift_tag_positions (tld->local_pb->command_buff, to_offset);
//
// Copy tagging records from the local command buffer and data
// from the local analysis buffer to the total-order buffers,
// and clear the local buffers afterwards.
//
buffer_drain_to (tld->local_pb->analysis_buff, tobs->pb->analysis_buff);
buffer_drain_to (tld->local_pb->command_buff, tobs->pb->command_buff);
//
// Increment the number of analysis requests in the thread-order
// analysis buffer. This number has to be kept up-to-date, because
// the JVM could end and we want to be able to flush the buffer
// properly.
//
tobs->analysis_count++;
buffer_put_jint (
tobs->pb->analysis_buff, tobs->analysis_count,
tobs->analysis_count_pos
);
//
// When the number of analysis requests in the buffer reaches a
// threshold, pass the payload to the tagger.
//
if (tobs->analysis_count >= ANALYSIS_COUNT) {
__enqueue_and_invalidate_payload (tobs);
}
}
exit_critical_section (jvmti, to_buff_lock);
}
void
glbuffer_sendall (jvmtiEnv * jvmti) {
//
// Flush all non-empty total-order buffers (under lock).
//
enter_critical_section (jvmti, to_buff_lock);
{
for (unsigned int i = 0; i < sizeof_array (to_buff_array); i++) {
to_buff_struct * tobs = &(to_buff_array [i]);
if (tobs->pb != NULL) {
__enqueue_and_invalidate_payload (tobs);
}
}
}
exit_critical_section (jvmti, to_buff_lock);
}
#ifndef _GLOBALBUFFER_H_
#define _GLOBALBUFFER_H_
#include "shared/procbuffs.h"
#include <jvmti.h>
// *** buffers for total ordering ***
typedef struct {
process_buffs * pb;
jint analysis_count;
size_t analysis_count_pos;
} to_buff_struct;
void glbuffer_init (jvmtiEnv * jvmti);
void glbuffer_commit (jvmtiEnv * jvmti);
void glbuffer_sendall (jvmtiEnv * jvmti);
#endif /* _GLOBALBUFFER_H_ */
#ifndef _JVMTIHELPER_H
#define _JVMTIHELPER_H
#ifndef ERR_PREFIX
#error ERR_PREFIX macro has to be deffined
#endif
#include <jvmti.h>
#include <stdlib.h>
// true/false consts
#define TRUE 1
#define FALSE 0
// error nums
#define ERR 10000
#define ERR_STD 10002
#define ERR_JVMTI 10003
/*
* Reports error if condition is true
*/
void check_error(int cond, const char *str) {
if (cond) {
fprintf(stderr, "%s%s\n", ERR_PREFIX, str);
exit(ERR);
}
}
/*
* Check error routine - reporting on one place
*/
void check_std_error(int retval, int errorval, const char *str) {
if (retval == errorval) {
static const int BUFFSIZE = 1024;
char msgbuf[BUFFSIZE];
snprintf(msgbuf, BUFFSIZE, "%s%s", ERR_PREFIX, str);
perror(msgbuf);
exit(ERR_STD);
}
}
/*
* Every JVMTI interface returns an error code, which should be checked
* to avoid any cascading errors down the line.
* The interface GetErrorName() returns the actual enumeration constant
* name, making the error messages much easier to understand.
*/
void check_jvmti_error(jvmtiEnv *jvmti, jvmtiError errnum, const char *str) {
if (errnum != JVMTI_ERROR_NONE) {
char *errnum_str;
errnum_str = NULL;
(void) (*jvmti)->GetErrorName(jvmti, errnum, &errnum_str);
fprintf(stderr, "%sJVMTI: %d(%s): %s\n", ERR_PREFIX, errnum,
(errnum_str == NULL ? "Unknown" : errnum_str),
(str == NULL ? "" : str));
exit(ERR_JVMTI);
}
}
/*
* Enter a critical section by doing a JVMTI Raw Monitor Enter
*/
void enter_critical_section(jvmtiEnv *jvmti, jrawMonitorID lock_id) {
jvmtiError error;
error = (*jvmti)->RawMonitorEnter(jvmti, lock_id);
check_jvmti_error(jvmti, error, "Cannot enter with raw monitor");
}
/*
* Exit a critical section by doing a JVMTI Raw Monitor Exit
*/
void exit_critical_section(jvmtiEnv *jvmti, jrawMonitorID lock_id) {
jvmtiError error;
error = (*jvmti)->RawMonitorExit(jvmti, lock_id);
check_jvmti_error(jvmti, error, "Cannot exit with raw monitor");
}
#endif /* _JVMTIHELPER_H */
#include "common.h"
#include "jvmtiutil.h"
#include <assert.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <jvmti.h>
//
#ifndef AGENT_NAME
#error AGENT_NAME macro has to be defined
#endif
#ifndef ERROR_JVMTI
#error ERROR_JVMTI macro has to be defined
#endif
//
/**
* Allocates JVM memory for the given buffer and copies the buffer
* into JVM memory.
*/
unsigned char *
jvmti_alloc_copy (jvmtiEnv * jvmti, const void * src, size_t size) {
assert (jvmti != NULL);
assert (src != NULL);
unsigned char * jvm_dst;
jvmtiError error = (*jvmti)->Allocate (jvmti, (jlong) size, &jvm_dst);
check_jvmti_error (jvmti, error, "failed create a JVM copy of a buffer");
memcpy (jvm_dst, src, size);
return jvm_dst;
}
/**
* Redefines a class given name and (partial) class definition.
* The class to be redefined is first looked up using JNI to
* complete the class definition information. Returns true
* if the redefinition was complete, false if the class could
* not be found.
*/
bool
jvmti_redefine_class (
jvmtiEnv * jvmti, JNIEnv * jni,
const char * class_name, const jvmtiClassDefinition * class_def
) {
assert (jvmti != NULL);
assert (jni != NULL);
assert (class_name != NULL);
assert (class_def != NULL);
jclass class = (* jni)->FindClass (jni, class_name);
if (class == NULL) {
return false;
}
//
jvmtiClassDefinition new_classdef = {
.klass = class,
.class_byte_count = class_def->class_byte_count,
.class_bytes = jvmti_alloc_copy (
jvmti, class_def->class_bytes, class_def->class_byte_count
),