...
 
Commits (62)
# Backup files
*~
# Generated dependency files
src-disl-agent/*.d
src-disl-agent/linux-*
src-disl-agent/windows-*
*.d
# Object fileds
*.o
# Compiled shared libraries
# Shared libraries
*.so
# 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,8 +58,9 @@ endif
# Source and object files needed to create the library
SOURCES = ../src-disl-agent/common.c ../src-disl-agent/jvmtiutil.c \
shared/buffer.c shared/buffpack.c shared/blockingqueue.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
......
#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 _DISLREAGENT_H
#define _DISLREAGENT_H
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *jvm, char *options, void *reserved);
#ifdef __cplusplus
}
#endif
#endif /* _DISLREAGENT_H */
#include "freehandler.h"
#include "shared/buffer.h"
#include "shared/buffpack.h"
#include "shared/bytebuffer.h"
#include "shared/bufferpack.h"
#include "shared/messagetype.h"
#include "pbmanager.h"
#include "sender.h"
#include "../src-disl-agent/jvmtiutil.h"
#include "jvmtiutil.h"
static jvmtiEnv * jvmti_env;
static jrawMonitorID obj_free_lock;
#define MAX_OBJ_FREE_EVENTS 4096
......@@ -18,67 +17,78 @@ 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 *env) {
jvmti_env = env;
jvmtiError error = (*jvmti_env)->CreateRawMonitor(jvmti_env, "obj free",
&obj_free_lock);
check_jvmti_error(jvmti_env, error, "Cannot create raw monitor");
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");
}
void fh_object_free(jlong tag) {
enter_critical_section(jvmti_env, obj_free_lock);
{
// 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 * buff = obj_free_buff->analysis_buff;
messager_objfree_item(buff, tag);
// update the number of free events
++obj_free_event_count;
buff_put_int(buff, obj_free_event_count_pos, obj_free_event_count);
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 send 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;
}
}
exit_critical_section(jvmti_env, obj_free_lock);
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);
}
void fh_send_buffer() {
// send object free buffer - with lock
enter_critical_section(jvmti_env, obj_free_lock);
{
if (obj_free_buff != NULL) {
sender_enqueue(obj_free_buff);
obj_free_buff = NULL;
}
}
exit_critical_section(jvmti_env, 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);
}
......@@ -3,9 +3,10 @@
#include <jvmti.h>
void fh_init(jvmtiEnv *env);
void fh_object_free(jlong tag);
void fh_send_buffer();
//
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/buffpack.h"
#include "shared/bufferpack.h"
#include "shared/messagetype.h"
#include "pbmanager.h"
#include "tagger.h"
#include "../src-disl-agent/jvmtiutil.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
#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];
static to_buff_struct to_buff_array [TO_BUFFER_COUNT];
static jvmtiEnv *jvmti_env;
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");
void glbuffer_init(jvmtiEnv *env) {
jvmti_env = env;
jvmtiError error;
// initialize total ordering buff array
for (unsigned int i = 0; i < sizeof_array (to_buff_array); i++) {
to_buff_array[i].pb = NULL;
}
}
error = (*jvmti_env)->CreateRawMonitor(jvmti_env, "buffids", &to_buff_lock);
check_jvmti_error(jvmti_env, error, "Cannot create raw monitor");
// initialize total ordering buff array
for (int i = 0; i < TO_BUFFER_COUNT; ++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 void glbuffer_new(to_buff_struct *tobs, tldata * tld) {
tobs->pb = pb_normal_get(tld->id);
// set owner_id as t_buffid
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 correct_cmd_buff_pos(buffer * cmd_buff, size_t shift) {
size_t cmd_buff_len = buffer_filled(cmd_buff);
size_t read = 0;
objtag_rec ot_rec;
// go through all records and shift the buffer position
while (read < cmd_buff_len) {
// read ot_rec data
buffer_read(cmd_buff, read, &ot_rec, sizeof(ot_rec));
// shift buffer position
ot_rec.buff_pos += shift;
// write ot_rec data
buffer_fill_at_pos(cmd_buff, read, &ot_rec, sizeof(ot_rec));
// next
read += sizeof(ot_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;
}
}
void glbuffer_commit() {
tldata * tld = tld_get();
enter_critical_section(jvmti_env, to_buff_lock);
{
// pointer to the total order buffer structure
to_buff_struct * tobs = &(to_buff_array[tld->to_buff_id]);
// allocate new buffer
if (tobs->pb == NULL) {
glbuffer_new(tobs, tld);
}
// first correct positions in command buffer
// records in command buffer are positioned according to the local
// analysis buffer but we want the position to be valid in total ordered
// buffer
correct_cmd_buff_pos(tld->local_pb->command_buff,
buffer_filled(tobs->pb->analysis_buff));
// fill total order buffers
buffer_fill(tobs->pb->analysis_buff,
// NOTE: normally access the buffer using methods
tld->local_pb->analysis_buff->buff,
tld->local_pb->analysis_buff->occupied);
buffer_fill(tobs->pb->command_buff,
// NOTE: normally access the buffer using methods
tld->local_pb->command_buff->buff,
tld->local_pb->command_buff->occupied);
// empty local buffers
buffer_clean(tld->local_pb->analysis_buff);
buffer_clean(tld->local_pb->command_buff);
// add number of completed requests
++(tobs->analysis_count);
// buffer has to be updated each time because jvm could end and buffer
// has to be up-to date
buff_put_int(tobs->pb->analysis_buff, tobs->analysis_count_pos,
tobs->analysis_count);
// send only when the method count is reached
if (tobs->analysis_count >= ANALYSIS_COUNT) {
// send buffers for object tagging
tagger_enqueue(tobs->pb);
// invalidate buffer pointer
tobs->pb = NULL;
}
}
exit_critical_section(jvmti_env, to_buff_lock);
/**
* 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;
}
void glbuffer_sendall() {
// send all total ordering buffers - with lock
enter_critical_section(jvmti_env, to_buff_lock);
{
for (int i = 0; i < TO_BUFFER_COUNT; ++i) {
// send all buffers for occupied ids
if (to_buff_array[i].pb != NULL) {
// send buffers for object tagging
tagger_enqueue(to_buff_array[i].pb);
// invalidate buffer pointer
to_buff_array[i].pb = NULL;
}
}
}
exit_critical_section(jvmti_env, to_buff_lock);
/**
* 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 <jvmti.h>
#include "shared/procbuffs.h"
#include "shared/buffer.h"
#include <jvmti.h>
// *** buffers for total ordering ***
typedef struct {
process_buffs * pb;
jint analysis_count;
size_t analysis_count_pos;
process_buffs * pb;
jint analysis_count;
size_t analysis_count_pos;
} to_buff_struct;
void glbuffer_init(jvmtiEnv *env);
void glbuffer_init (jvmtiEnv * jvmti);
void glbuffer_commit();
void glbuffer_commit (jvmtiEnv * jvmti);
void glbuffer_sendall();
void glbuffer_sendall (jvmtiEnv * jvmti);
#endif /* _GLOBALBUFFER_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
),
};
jvmtiError error = (*jvmti)->RedefineClasses (jvmti, 1, &new_classdef);
check_jvmti_error (jvmti, error, "failed to redefine class");
return true;
}
static char *
__get_system_property (jvmtiEnv * jvmti, const char * name) {
//
// If the requested property does not exist, GetSystemProperty() will
// return JVMTI_ERROR_NOT_AVAILABLE and will not modify the value pointer.
// The other error that could occur is JVMTI_ERROR_NULL_POINTER, but we
// assert that could not happen.
//
char * value = NULL;
(*jvmti)->GetSystemProperty (jvmti, name, &value);
if (value == NULL) {
return NULL;
}
//
char * result = strdup (value);
check_error (result == NULL, "failed to duplicate system property value");
jvmtiError error = (*jvmti)->Deallocate (jvmti, (unsigned char *) value);
check_jvmti_error (jvmti, error, "failed to deallocate system property value");
return result;
}
static bool
__parse_bool (const char * strval) {
static const char * trues [] = { "true", "yes", "on", "1" };
return find_value_index (strval, trues, sizeof_array (trues)) >= 0;
}
/**
* Returns the boolean value of a system property, or the default
* value if it not defined.
*/
bool
jvmti_get_system_property_bool (
jvmtiEnv * jvmti, const char * name, bool dflval
) {
assert (jvmti != NULL);
assert (name != NULL);
char * strval = __get_system_property (jvmti, name);
if (strval != NULL) {
bool result = __parse_bool (strval);
free (strval);
return result;
} else {
return dflval;
}
}
/**
* Returns the string value of a system property, or the default
* value if the property is not defined. The memory for the returned
* value is always allocated (even for the default value) and the
* caller is responsible for releasing it.
*/
char *
jvmti_get_system_property_string (
jvmtiEnv * jvmti, const char * name, const char * dflval
) {
assert (jvmti != NULL);
assert (name != NULL);
char * strval = __get_system_property (jvmti, name);
if (strval != NULL) {
return strval;
} else if (dflval != NULL) {
//
// Duplicate the default value so that the caller always "owns"
// the returned value and can release it using free().
//
char * result = strdup (dflval);
check_error (result == NULL, "failed to duplicate default value");
return result;
} else {
return NULL;
}
}
/**
* Returns the current VM phase. Can be called at any VM phase.
*/
jvmtiPhase
jvmti_get_phase (jvmtiEnv * jvmti) {
jvmtiPhase phase;
jvmtiError error = (*jvmti)->GetPhase (jvmti, &phase);
check_jvmti_error (jvmti, error, "failed to get VM phase");
return phase;
}
/**
* Returns the current thread. This method can
* only be called during the START and LIVE phases
*/
jthread
jvmti_get_current_thread (jvmtiEnv * jvmti) {
jthread thread = NULL;
jvmtiError error = (*jvmti)->GetCurrentThread (jvmti, &thread);
check_jvmti_error (jvmti, error, "failed to get current thread");
return thread;
}
/**
* Returns the size of the given object in bytes. This method can
* only be called during the START and LIVE phases.
*/
jlong
jvmti_get_object_size (jvmtiEnv * jvmti, jobject object) {
jlong size = -1;
jvmtiError error = (*jvmti)->GetObjectSize (jvmti, object, &size);
check_jvmti_error (jvmti, error, "failed to get object size");
return size;
}
/**
* Gets tag from the given object. This method can only be called
* during the START and LIVE phases.
*/
jlong
jvmti_get_object_tag (jvmtiEnv * jvmti, jobject object) {
jlong tag;
jvmtiError error = (*jvmti)->GetTag (jvmti, object, &tag);
check_jvmti_error (jvmti, error, "failed to get object tag");
return tag;
}
/**
* Sets a tag to the given object. This method can only be called
* during the START and LIVE phases.
*/
jlong
jvmti_set_object_tag (jvmtiEnv * jvmti, jobject object, jlong tag) {
jvmtiError error = (*jvmti)->SetTag (jvmti, object, tag);
check_jvmti_error (jvmti, error, "failed to set object tag");
return tag;
}
/**
* Reports a JVMTI error and terminates the program. This function implements
* the slow path of check_jvmti_error() and prints the given error message
* along with a JVMTI error name obtained using the GetErrorName() JVMTI
* interface.
*/
void
die_with_jvmti_error (
jvmtiEnv * jvmti, jvmtiError error,
const char * restrict format, va_list args
) {
// JVMTI error
char * cause = NULL;
(void) (*jvmti)->GetErrorName (jvmti, error, &cause);
fprintf (
stderr, AGENT_NAME ": jvmti-error: %s (%d)\n",
(cause != NULL) ? cause : "unknown", error
);
(*jvmti)->Deallocate (jvmti, (unsigned char *) cause);
// agent error
fprintf (stderr, AGENT_NAME ": ");
vfprintf (stderr, format, args);
fprintf (stderr, "\n");
exit (ERROR_JVMTI);
}
#ifndef _JVMTIUTIL_H_
#define _JVMTIUTIL_H_
#include "common.h"
#include <stdarg.h>
#include <stdbool.h>
#include <stdlib.h>
#include <jvmti.h>
//
unsigned char * jvmti_alloc_copy (jvmtiEnv * jvmti, const void * src, size_t size);
bool jvmti_redefine_class (
jvmtiEnv * jvmti, JNIEnv * jni,
const char * name, const jvmtiClassDefinition * definition
);
bool jvmti_get_system_property_bool (
jvmtiEnv * jvmti, const char * name, bool dflval
);
char * jvmti_get_system_property_string (
jvmtiEnv * jvmti, const char * name, const char * dflval
);
jthread jvmti_get_current_thread (jvmtiEnv * jvmti);
jlong jvmti_get_object_size (jvmtiEnv * jvmti, jobject object);
jlong jvmti_get_object_tag (jvmtiEnv * jvmti, jobject object);
jlong jvmti_set_object_tag (jvmtiEnv * jvmti, jobject object, jlong tag);
//
void die_with_jvmti_error (
jvmtiEnv * jvmti, jvmtiError error, const char * format, va_list args
);
/**
* Checks whether a JVMTI invocation returned an error. Every JVMTI interface
* returns an error code, which should be checked to avoid any cascading errors
* down the line.
*/
static inline void
check_jvmti_error (
jvmtiEnv * restrict jvmti, jvmtiError errnum,
const char * restrict format, ...
) {
if (errnum != JVMTI_ERROR_NONE) {
va_list args;
va_start (args, format);
die_with_jvmti_error (jvmti, errnum, format, args);
va_end (args);
}
}
/**
* Enters a critical section protected by a JVMTI Raw Monitor.
*/
static inline void
enter_critical_section (jvmtiEnv *jvmti, jrawMonitorID lock_id) {
jvmtiError error = (*jvmti)->RawMonitorEnter(jvmti, lock_id);
check_jvmti_error (jvmti, error, "failed to enter critical section");
}
/**
* Leaves a critical section protected by a JVMTI Raw Monitor.
*/
static inline void
exit_critical_section (jvmtiEnv *jvmti, jrawMonitorID lock_id) {
jvmtiError error = (*jvmti)->RawMonitorExit(jvmti, lock_id);
check_jvmti_error (jvmti, error, "failed to exit critical section");
}
#endif /* _JVMTIUTIL_H_ */
This diff is collapsed.
#ifndef _NETREF_H
#define _NETREF_H
#define _NETREF_H
#include "shared/bytebuffer.h"
#include <stdbool.h>
#include <jvmti.h>
#include <jni.h>
#include "shared/buffer.h"
//
#define NULL_NET_REF 0
// ******************* Net reference routines *******************
unsigned char net_ref_get_spec(jlong net_ref);
void net_ref_set_spec(jlong * net_ref, unsigned char spec);
// only retrieves object tag data
jlong get_tag(jvmtiEnv * jvmti_env, jobject obj);
bool netref_get_has_data (jlong netref);
void netref_set_has_data (jlong * netref, bool has_data);
// 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);
jlong get_net_reference (
JNIEnv * jni, jvmtiEnv * jvmti, buffer_t * new_obj_buff, jobject object
);
// !!! 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);
void update_net_reference (jvmtiEnv * jvmti, jobject object, jlong netref);
#endif /* _NETREF_H */
#endif /* _NETREF_H */
......@@ -3,7 +3,7 @@
#include "shared/blockingqueue.h"
#include "shared/threadlocal.h"
#include "../src-disl-agent/jvmtiutil.h"
#include "jvmtiutil.h"
// queue with empty buffers
static blocking_queue empty_q;
......@@ -12,136 +12,150 @@ static blocking_queue empty_q;
static blocking_queue utility_q;
// list of all allocated bq buffers
process_buffs pb_list[BQ_BUFFERS + BQ_UTILITY];
void pb_init() {
bq_create(&utility_q, BQ_UTILITY, sizeof(process_buffs *));
bq_create(&empty_q, BQ_BUFFERS, sizeof(process_buffs *));
for (int i = 0; i < BQ_BUFFERS + BQ_UTILITY; i++) {
process_buffs * pb = &(pb_list[i]);
// allocate process_buffs
pb->analysis_buff = malloc(sizeof(buffer));
buffer_alloc(pb->analysis_buff);
pb->command_buff = malloc(sizeof(buffer));
buffer_alloc(pb->command_buff);
if (i < BQ_BUFFERS) {
// add buffer to the empty queue
pb_normal_release(pb);
} else {
// add buffer to the utility queue
pb_utility_release(pb);
}
}
}
process_buffs pb_list [BQ_BUFFERS + BQ_UTILITY];
void pb_free() {
// NOTE: Buffers hold by other threads can be in inconsistent state.
// We cannot simply send them, so we at least inform the user.
// inform about all non-send buffers
// all buffers should be send except some daemon thread buffers
// - also some class loading + thread tagging buffers can be there (with 0)
// Report: .
int relevant_count = 0;
int support_count = 0;
int marked_thread_count = 0;
int non_marked_thread_count = 0;