jvmtiutil.c 4.4 KB
Newer Older
1 2 3
#include "common.h"
#include "jvmtiutil.h"

4 5 6 7 8 9 10
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <jvmti.h>

11
//
12 13 14 15 16 17 18 19 20

#ifndef ERROR_PREFIX
#error ERROR_PREFIX macro has to be defined
#endif

#ifndef ERROR_JVMTI
#error ERROR_JVMTI macro has to be defined
#endif

21
//
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190

/**
 * 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;
	}
}


/**
 * 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 errnum, const char *str) {
	char * errnum_str = NULL;
	(void) (*jvmti)->GetErrorName (jvmti, errnum, &errnum_str);

	fprintf (
		stderr, "%sJVMTI: %d (%s): %s\n",
		ERROR_PREFIX, errnum,
		(errnum_str == NULL ? "Unknown" : errnum_str),
		(str == NULL ? "" : str)
	);

	exit (ERROR_JVMTI);
}