Commit 438b5748 authored by Lubomir Bulej's avatar Lubomir Bulej

Merge branch 'fasttag-cleanup'

parents 6b4a7e01 80b1aef4
Pipeline #4513 passed with stages
in 3 minutes and 23 seconds
# 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