Commit ab60654e authored by Lubomir Bulej's avatar Lubomir Bulej

DiSL-Agent: Fixed problem with partial reads of the response.

DiSL-Agent: Revamped message creation and handling.
DiSL-Agent: Various cleanups.
parent 2bfcd547
......@@ -35,7 +35,7 @@ LINK_SHARED=$(LINK.c) -shared -o $@
COMMON_FLAGS=-fPIC -std=gnu99
# Options that help find errors
COMMON_FLAGS+= -W -Wall -Wextra -Wno-unused-parameter
COMMON_FLAGS+= -W -Wall -Wextra -Wno-unused-parameter -lpthread
CFLAGS += $(COMMON_FLAGS)
......@@ -44,7 +44,7 @@ CFLAGS += -I$(JAVA_HOME)/include -I$(JAVA_HOME)/include/linux
# add debugging output
ifneq ($(DEBUG),)
CFLAGS += -DDEBUG
CFLAGS += -DDEBUG -g
else
CFLAGS += -DNDEBUG -O3
endif
......
......@@ -6,9 +6,9 @@
/**
* Reports an actual general error. This function implements the slow path of
* check_error(). It prints the given error message and exits with an error code
* indicating a general error.
* 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 general error.
*/
void
report_error (const char * message) {
......@@ -18,17 +18,15 @@ report_error (const char * message) {
/**
* Reports an actual standard library error. 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.
* 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
report_std_error (const char * message) {
char msgbuf [1024];
snprintf (msgbuf, sizeof (msgbuf), "%s%s", ERROR_PREFIX, message);
perror (msgbuf);
fprintf (stderr, "%s%s\n", ERROR_PREFIX, message);
perror ("cause");
exit (ERROR_STD);
}
......
......@@ -10,7 +10,7 @@
#define ERROR_STD 10002
#define ERROR_JVMTI 10003
#define ERROR_PREFIX "DiSL agent error: "
#define ERROR_PREFIX "DiSL-agent error: "
void report_error (const char * message);
......@@ -25,8 +25,8 @@ void report_std_error (const char * message);
/**
* Checks for a general error by testing the results of an error condition.
* Reports a general error and terminates the program if the condition is true.
* Reports a general error and terminates the program if the provided
* error condition is true.
*/
inline static void
check_error (bool error, const char * message) {
......@@ -37,13 +37,12 @@ check_error (bool error, const char * message) {
/**
* Checks the return value of a standard library call for error. Reports a standard
* library error and terminates the program if the the returned value matches the
* specified error value.
* Reports a standard library error and terminates the program if the provided
* error condition is true.
*/
inline static void
check_std_error (int retval, int errorval, const char * message) {
if (retval == errorval) {
check_std_error (bool error, const char * message) {
if (error) {
report_std_error (message);
}
}
......
......@@ -17,12 +17,12 @@
static void
__connection_init (struct connection * connection, const int sockfd) {
connection->sockfd = sockfd;
list_init (& connection->cp_link);
list_init (&connection->cp_link);
#ifdef DEBUG
connection->sent_bytes = 0;
connection->recv_bytes = 0;
#endif /* DEBUG */
#endif
}
......@@ -36,17 +36,17 @@ struct connection * connection_open (struct addrinfo * addr) {
// sender side and create a wrapper object for the connection.
//
int sockfd = socket(addr->ai_family, SOCK_STREAM, 0);
check_std_error(sockfd, -1, "failed to create socket");
check_std_error (sockfd < 0, "failed to create socket");
int connect_result = connect(sockfd, addr->ai_addr, addr->ai_addrlen);
check_std_error(connect_result, -1, "failed to connect to server");
check_std_error (connect_result < 0, "failed to connect to server");
int tcp_nodelay = 1;
int sso_result = setsockopt (
sockfd, IPPROTO_TCP, TCP_NODELAY,
&tcp_nodelay, sizeof (tcp_nodelay)
);
check_std_error(sso_result, -1, "failed to enable TCP_NODELAY");
check_std_error (sso_result < 0, "failed to enable TCP_NODELAY");
//
......@@ -67,11 +67,11 @@ connection_close (struct connection * connection) {
assert (connection != NULL);
#if DEBUG
fprintf (
stderr, "socket %d: sent bytes %llu, recv bytes %llu\n",
printf (
"debug: socket %d: sent bytes %llu, recv bytes %llu\n",
connection->sockfd, connection->sent_bytes, connection->recv_bytes
);
#endif /* DEBUG */
#endif
close (connection->sockfd);
free (connection);
......@@ -79,101 +79,103 @@ connection_close (struct connection * connection) {
//
static void
__socket_send (const int sockfd, const void * buf, const ssize_t len) {
typedef ssize_t (* xfer_fn) (int sockfd, void * buf, size_t len, int flags);
inline static ssize_t
__socket_xfer (xfer_fn xfer, const int sockfd, const void * buf, const ssize_t len) {
unsigned char * buf_tail = (unsigned char *) buf;
size_t remaining = len;
while (remaining > 0) {
int sent = send (sockfd, buf_tail, remaining, 0);
check_std_error (sent, -1, "error sending data to server");
ssize_t xferred = xfer (sockfd, buf_tail, remaining, 0);
if (xferred < 0) {
return -remaining;
}
remaining -= sent;
buf_tail += sent;
remaining -= xferred;
buf_tail += xferred;
}
return len;
}
/**
* Sends data into the given connection.
* Sends data into the given connection. Does not return until all provided
* data has been sent.
*/
void
ssize_t
connection_send (struct connection * connection, const void * buf, const ssize_t len) {
assert (connection != NULL);
assert (buf != NULL);
assert (len >= 0);
__socket_send (connection->sockfd, buf, len);
ssize_t sent = __socket_xfer ((xfer_fn) send, connection->sockfd, buf, len);
check_std_error (sent < 0, "error sending data to server");
#ifdef DEBUG
connection->sent_bytes += len;
#endif /* DEBUG*/
connection->sent_bytes += sent;
#endif
return sent;
}
/**
* Sends vectored data into the given connection.
* Sends vectored data into the given connection. May send less data than requested.
*/
void
ssize_t
connection_send_iov (struct connection * connection, const struct iovec * iov, int iovcnt) {
assert (connection != NULL);
assert (iov != NULL);
ssize_t written = writev (connection->sockfd, iov, iovcnt);
check_std_error (written, -1, "error sending data to server");
ssize_t sent = writev (connection->sockfd, iov, iovcnt);
check_std_error (sent < 0, "error sending data to server");
#ifdef DEBUG
connection->sent_bytes += written;
#endif /* DEBUG */
}
//
static void
__socket_recv (const int sockfd, void * buf, ssize_t len) {
unsigned char * buf_tail = (unsigned char *) buf;
ssize_t remaining = len;
connection->sent_bytes += sent;
#endif
while (remaining > 0) {
int received = recv (sockfd, buf_tail, remaining, 0);
check_std_error(received, -1, "error receiving data from server");
remaining -= received;
buf_tail += received;
}
check_error (remaining < 0, "received more data than expected");
return sent;
}
//
/**
* Receives a predefined amount of data from the given connection.
* Receives a predefined amount of data from the given connection. Does not return
* until all requested data has been received.
*/
void
ssize_t
connection_recv (struct connection * connection, void * buf, const ssize_t len) {
assert (connection != NULL);
assert (buf != NULL);
assert (len >= 0);
__socket_recv (connection->sockfd, buf, len);
ssize_t received = __socket_xfer ((xfer_fn) recv, connection->sockfd, buf, len);
check_std_error (received < 0, "error receiving data from server");
#ifdef DEBUG
connection->recv_bytes += len;
#endif /* DEBUG */
connection->recv_bytes += received;
#endif
return received;
}
/**
* Receives vectored data from the given connection.
* Receives vectored data from the given connection. May receive less data than requested.
*/
void
ssize_t
connection_recv_iov (struct connection * connection, const struct iovec * iov, int iovcnt) {
assert (connection != NULL);
assert (iov != NULL);
ssize_t read = readv (connection->sockfd, iov, iovcnt);
check_std_error (read, -1, "error receiving data from server");
ssize_t received = readv (connection->sockfd, iov, iovcnt);
check_std_error (received < 0, "error receiving data from server");
#ifdef DEBUG
connection->recv_bytes += read;
#endif /* DEBUG */
connection->recv_bytes += received;
#endif
return received;
}
......@@ -28,9 +28,10 @@ struct connection {
struct connection * connection_open (struct addrinfo * addr);
void connection_close (struct connection * connection);
void connection_send (struct connection * connection, const void * buf, const ssize_t len);
void connection_send_iov (struct connection * connection, const struct iovec * iov, int iovcnt);
void connection_recv (struct connection * connection, void * buf, const ssize_t len);
void connection_recv_iov (struct connection * connection, const struct iovec * iov, int iovcnt);
ssize_t connection_send (struct connection * connection, const void * buf, const ssize_t len);
ssize_t connection_send_iov (struct connection * connection, const struct iovec * iov, int iovcnt);
ssize_t connection_recv (struct connection * connection, void * buf, const ssize_t len);
ssize_t connection_recv_iov (struct connection * connection, const struct iovec * iov, int iovcnt);
#endif /* _CONNECTION_H_ */
......@@ -15,8 +15,8 @@ connection_pool_init (struct connection_pool * cp, struct addrinfo * endpoint) {
assert (endpoint != NULL);
cp->connections_count = 0;
list_init (& cp->free_connections);
list_init (& cp->busy_connections);
list_init (&cp->free_connections);
list_init (&cp->busy_connections);
cp->endpoint = endpoint;
cp->after_open_hook = NULL;
......@@ -52,9 +52,9 @@ connection_pool_get_connection (struct connection_pool * cp) {
// Grab the first available connection and return. If there is no connection
// available, create a new one and add it to the busy connection list.
//
if (!list_is_empty (& cp->free_connections)) {
struct list * item = list_remove_after (& cp->free_connections);
list_insert_after (item, & cp->busy_connections);
if (!list_is_empty (&cp->free_connections)) {
struct list * item = list_remove_after (&cp->free_connections);
list_insert_after (item, &cp->busy_connections);
return list_item (item, struct connection, cp_link);
} else {
......@@ -63,8 +63,11 @@ connection_pool_get_connection (struct connection_pool * cp) {
cp->after_open_hook (connection);
}
list_insert_after (& connection->cp_link, & cp->busy_connections);
list_insert_after (&connection->cp_link, &cp->busy_connections);
cp->connections_count++;
#ifdef DEBUG
printf ("[new connection, %d in total] ", cp->connections_count);
#endif
return connection;
}
}
......@@ -85,8 +88,8 @@ connection_pool_put_connection (
// Move the connection from the list of busy connections to
// the list of available connections.
//
struct list * item = list_remove (& connection->cp_link);
list_insert_after (item, & cp->free_connections);
struct list * item = list_remove (&connection->cp_link);
list_insert_after (item, &cp->free_connections);
}
//
......@@ -114,15 +117,15 @@ connection_pool_close (struct connection_pool * cp) {
assert (cp != NULL);
#ifdef DEBUG
fprintf (
stderr, "connection pool %s: max connections %d\n",
printf (
"debug: connection pool for %s: max connections %d\n",
cp->endpoint->ai_canonname, cp->connections_count
);
#endif /* DEBUG */
#endif
list_destroy (& cp->free_connections, __connection_destructor, (void *) cp);
if (!list_is_empty (& cp->busy_connections)) {
list_destroy (&cp->free_connections, __connection_destructor, (void *) cp);
if (!list_is_empty (&cp->busy_connections)) {
fprintf (stderr, "warning: closing %d active connections", cp->connections_count);
list_destroy (& cp->busy_connections, __connection_destructor, (void *) cp);
list_destroy (&cp->busy_connections, __connection_destructor, (void *) cp);
}
}
This diff is collapsed.
......@@ -17,18 +17,24 @@
#endif
/**
* 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_env, JNIEnv * jni_env,
jvmtiEnv * jvmti, JNIEnv * jni,
const char * class_name, const jvmtiClassDefinition * class_def
) {
assert (jni_env != NULL);
assert (jvmti_env != NULL);
assert (jvmti != NULL);
assert (jni != NULL);
assert (class_name != NULL);
assert (class_def != NULL);
jclass class = (* jni_env)->FindClass (jni_env, class_name);
jclass class = (* jni)->FindClass (jni, class_name);
if (class == NULL) {
return false;
}
......@@ -38,8 +44,8 @@ jvmti_redefine_class (
jvmtiClassDefinition new_classdef = * class_def;
new_classdef.klass = class;
jvmtiError error = (*jvmti_env)->RedefineClasses (jvmti_env, 1, &new_classdef);
check_jvmti_error (jvmti_env, error, "failed to redefine class");
jvmtiError error = (*jvmti)->RedefineClasses (jvmti, 1, &new_classdef);
check_jvmti_error (jvmti, error, "failed to redefine class");
return true;
}
......@@ -55,7 +61,7 @@ __get_system_property (jvmtiEnv * jvmti, const char * name) {
// assert that could not happen.
//
char * value = NULL;
(*jvmti)->GetSystemProperty (jvmti, name, & value);
(*jvmti)->GetSystemProperty (jvmti, name, &value);
if (value == NULL) {
return NULL;
......@@ -73,7 +79,6 @@ __get_system_property (jvmtiEnv * jvmti, const char * name) {
}
static bool
__parse_bool (const char * strval) {
static const char * trues [] = { "true", "yes", "on", "1" };
......@@ -81,6 +86,10 @@ __parse_bool (const char * strval) {
}
/**
* 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
......@@ -101,6 +110,12 @@ jvmti_get_system_property_bool (
}
/**
* 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
......@@ -118,7 +133,7 @@ jvmti_get_system_property_string (
// the returned value and can release it using free().
//
char * result = strdup (dflval);
check_error (result == NULL, "failed to duplicate property value");
check_error (result == NULL, "failed to duplicate default value");
return result;
} else {
......@@ -128,9 +143,10 @@ jvmti_get_system_property_string (
/**
* Reports an actual JVMTI error. 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.
* 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
report_jvmti_error (jvmtiEnv *jvmti, jvmtiError errnum, const char *str) {
......
......@@ -39,8 +39,8 @@ typedef int (* list_match_fn) (struct list * item, void * data);
*/
#define LIST_INIT(name) \
{ \
.prev = & (name), \
.next = & (name) \
.prev = &(name), \
.next = &(name) \
}
......@@ -341,7 +341,7 @@ list_remove_before (struct list * item)
assert ((head) != NULL); \
for ( \
curr = list_item ((head)->next, typeof (* curr), member); \
(& curr->member) != (head); \
(&curr->member) != (head); \
curr = list_item (curr->member.next, typeof (* curr), member) \
)
......@@ -360,7 +360,7 @@ list_remove_before (struct list * item)
assert ((head) != NULL); \
for ( \
curr = list_item ((head)->prev, typeof (* curr), member); \
(& curr->member) != (head); \
(&curr->member) != (head); \
curr = list_item (curr->member.prev, typeof (* curr), member) \
)
......
......@@ -2,119 +2,82 @@
#include <stdlib.h>
#include <string.h>
#include <sys/uio.h>
#include <jni.h>
#include "common.h"
#include "msgchannel.h"
#include "connection.h"
struct message
create_message (
jint message_flags,
const unsigned char * control, jint control_size,
const unsigned char * classcode, jint classcode_size
) {
struct message result;
result.message_flags = message_flags;
// control + size
result.control_size = control_size;
// contract: (if control_size <= 0) pointer may be copied (stolen)
if (control != NULL && control_size > 0) {
// without ending 0
unsigned char * buffcn = (unsigned char *) malloc(control_size);
memcpy(buffcn, control, control_size);
result.control = buffcn;
} else {
result.control = control;
result.control_size = abs(control_size);
}
// class code + size
result.classcode_size = classcode_size;
// contract: (if classcode_size <= 0) pointer may be copied (stolen)
if(classcode != NULL && classcode_size > 0) {
unsigned char * buffcc = (unsigned char *) malloc(classcode_size);
memcpy(buffcc, classcode, classcode_size);
result.classcode = buffcc;
} else {
result.classcode = classcode;
result.classcode_size = abs(classcode_size);
}
return result;
}
void
free_message(struct message * msg) {
if(msg->control != NULL) {
// cast because of const
free((void *) msg->control);
msg->control = NULL;
msg->control_size = 0;
}
if(msg->classcode != NULL) {
// cast because of const
free((void *) msg->classcode);
msg->classcode = NULL;
msg->classcode_size = 0;
}
}
/**
* Sends a request message to the remote instrumentation server.
* Sends a message to the remote instrumentation server.
*/
void
send_message (struct connection * conn, struct message * msg) {
message_send (struct connection * conn, struct message * msg) {
assert (conn != NULL);
assert (msg != NULL);
#ifdef DEBUG
printf (
"Sending - control: %d, code: %d ... ",
msg->control_size, msg->classcode_size
"debug: sending message: flags %08x, control %d, code %d ... ",
msg->message_flags, msg->control_size, msg->classcode_size
);
#endif
jint ints [3];
ints [0] = htonl (msg->message_flags);
ints [1] = htonl (msg->control_size);
ints [2] = htonl (msg->classcode_size);
jint ints [] = {
htonl (msg->message_flags),
htonl (msg->control_size),
htonl (msg->classcode_size)
};
struct iovec iovs [3];
iovs [0].iov_base = &ints [0];
iovs [0].iov_len = sizeof (ints);
iovs [1].iov_base = (void *) msg->control;
iovs [1].iov_len = msg->control_size;
iovs [2].iov_base = (void *) msg->classcode;
iovs [2].iov_len = msg->classcode_size;
struct iovec iovs [] = {
{ .iov_base = &ints [0], .iov_len = sizeof (ints) },
{ .iov_base = (void *) msg->control, .iov_len = msg->control_size },
{ .iov_base = (void *) msg->classcode, .iov_len = msg->classcode_size }
};
connection_send_iov (conn, &iovs [0], sizeof_array (iovs));
// TODO Handle the possibility of partial write.
ssize_t sent = connection_send_iov (conn, &iovs [0], sizeof_array (iovs));
assert (sent == (ssize_t) (sizeof (ints) + msg->control_size + msg->classcode_size));
#ifdef DEBUG
printf("done\n");
printf ("done\n");
#endif
}
static inline unsigned char *
__alloc_bytes (size_t len) {
__alloc_buffer (size_t len) {
//
// Allocated a buffer with an extra (zeroed) byte, but only if the
// requested buffer length is greater than zero. Return NULL otherwise.
//
if (len == 0) {
return NULL;
}
//
unsigned char * buf = (unsigned char *) malloc (len + 1);
check_error (buf == NULL, "failed to allocate receive buffer");
check_error (buf == NULL, "failed to allocate buffer");
buf [len] = '\0';
return buf;
}
/**
* Receives a response message from the remote instrumentation server.
* Receives a message from the remote instrumentation server.
*/
struct message
recv_message (struct connection * conn) {
void
message_recv (struct connection * conn, struct message * msg) {
assert (conn != NULL);
assert (msg != NULL);
#ifdef DEBUG
printf("Receiving ");
printf ("debug: receiving message: ");
#endif
//
......@@ -125,29 +88,34 @@ recv_message (struct connection * conn) {
jint ints [3];
connection_recv (conn, &ints [0], sizeof (ints));
//
jint response_flags = ntohl (ints [0]);
jint control_size = ntohl (ints [1]);
jint classcode_size = ntohl (ints [2]);
unsigned char * control = __alloc_bytes (control_size);
unsigned char * classcode = __alloc_bytes (classcode_size);
//
unsigned char * control = __alloc_buffer (control_size);
if (control_size > 0) {
connection_recv (conn, control, control_size);
}
unsigned char * classcode = __alloc_buffer (classcode_size);
if (classcode_size > 0) {
connection_recv (conn, classcode, classcode_size);
}
struct iovec iovs [2];
iovs [0].iov_base = control;
iovs [0].iov_len = control_size;
iovs [1].iov_base = classcode;
iovs [1].iov_len = classcode_size;
//
connection_recv_iov (conn, &iovs [0], sizeof_array (iovs));
msg->message_flags = response_flags;
msg->control_size = control_size;
msg->classcode_size = classcode_size;
msg->control = control;
msg->classcode = classcode;
#ifdef DEBUG
printf("- control: %d, code: %d ... done\n", control_size, classcode_size);
printf (
"flags %08x, control %d, code %d ... done\n",
response_flags, control_size, classcode_size
);
#endif
// negative length - create_message adopts pointers
return create_message(response_flags, control, -control_size, classcode, -classcode_size);
}
......@@ -15,15 +15,7 @@ struct message {
};
struct message create_message (
jint message_flags,
const unsigned char * control, jint control_size,
const unsigned char * classcode, jint