Home | History | Annotate | Download | only in jvmti
      1 /**
      2  * @file jvmti_oprofile.c
      3  * JVMTI agent implementation to report jitted JVM code to Oprofile
      4  *
      5  * @remark Copyright 2007 OProfile authors
      6  *
      7  * This library is free software; you can redistribute it and/or
      8  * modify it under the terms of the GNU Lesser General Public
      9  * License as published by the Free Software Foundation; either
     10  * version 2.1 of the License, or (at your option) any later version.
     11  *
     12  * This library is distributed in the hope that it will be useful,
     13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15  * Lesser General Public License for more details.
     16  *
     17  * You should have received a copy of the GNU Lesser General Public
     18  * License along with this library; if not, write to the Free Software
     19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     20  *
     21  * @author Jens Wilke
     22  * @Modifications Daniel Hansel
     23  *
     24  * Copyright IBM Corporation 2007
     25  *
     26  */
     27 
     28 #include <stdio.h>
     29 #include <jvmti.h>
     30 #include <string.h>
     31 #include <stdint.h>
     32 #include <stdlib.h>
     33 #include <errno.h>
     34 
     35 #include "opagent.h"
     36 
     37 static int debug = 0;
     38 static int can_get_line_numbers = 0;
     39 static op_agent_t agent_hdl;
     40 
     41 /**
     42  * Handle an error or a warning, return 0 if the checked error is
     43  * JVMTI_ERROR_NONE, i.e. success
     44  */
     45 static int handle_error(jvmtiError err, char const * msg, int severe)
     46 {
     47 	if (err != JVMTI_ERROR_NONE) {
     48 		fprintf(stderr, "%s: %s, err code %i\n",
     49 			severe ? "Error" : "Warning", msg, err);
     50 	}
     51 	return err != JVMTI_ERROR_NONE;
     52 }
     53 
     54 
     55 /**
     56  * returned array is map_length length, params map and map_length != 0
     57  * format of lineno information is JVMTI_JLOCATION_JVMBCI, map is an array
     58  * of { address, code byte index }, table_ptr an array of { byte code index,
     59  * lineno }
     60  */
     61 static struct debug_line_info *
     62 create_debug_line_info(jint map_length, jvmtiAddrLocationMap const * map,
     63 		       jint entry_count, jvmtiLineNumberEntry* table_ptr,
     64 		       char const * source_filename)
     65 {
     66 	struct debug_line_info * debug_line;
     67 	int i, j;
     68 	if (debug) {
     69 		fprintf(stderr, "Source %s\n", source_filename);
     70 		for (i = 0; i < map_length; ++i) {
     71 			fprintf(stderr, "%p %lld\t",
     72 			        map[i].start_address,
     73 			        (long long)map[i].location);
     74 		}
     75 		fprintf(stderr, "\n");
     76 		for (i = 0; i < entry_count; ++i) {
     77 			fprintf(stderr, "%lld %d\t",
     78 				(long long)table_ptr[i].start_location,
     79 				table_ptr[i].line_number);
     80 		}
     81 		fprintf(stderr, "\n");
     82 	}
     83 
     84 	debug_line = calloc(map_length, sizeof(struct debug_line_info));
     85 	if (!debug_line)
     86 		return 0;
     87 
     88 	for (i = 0; i < map_length; ++i) {
     89 		/* FIXME: likely to need a lower_bound on the array, but
     90 		 * documentation is a bit obscure about the contents of these
     91 		 * arrray
     92 		 **/
     93 		for (j = 0; j < entry_count - 1; ++j) {
     94 			if (table_ptr[j].start_location > map[i].location)
     95 				break;
     96 		}
     97 		debug_line[i].vma = (unsigned long)map[i].start_address;
     98 		debug_line[i].lineno = table_ptr[j].line_number;
     99 		debug_line[i].filename = source_filename;
    100 	}
    101 
    102 	if (debug) {
    103 		for (i = 0; i < map_length; ++i) {
    104 			fprintf(stderr, "%lx %d\t", debug_line[i].vma,
    105 				debug_line[i].lineno);
    106 		}
    107 		fprintf(stderr, "\n");
    108 	}
    109 
    110 	return debug_line;
    111 }
    112 
    113 
    114 static void JNICALL cb_compiled_method_load(jvmtiEnv * jvmti,
    115 	jmethodID method, jint code_size, void const * code_addr,
    116 	jint map_length, jvmtiAddrLocationMap const * map,
    117 	void const * compile_info)
    118 {
    119 	jclass declaring_class;
    120 	char * class_signature = NULL;
    121  	char * method_name = NULL;
    122  	char * method_signature = NULL;
    123 	jvmtiLineNumberEntry* table_ptr = NULL;
    124 	char * source_filename = NULL;
    125 	struct debug_line_info * debug_line = NULL;
    126  	jvmtiError err;
    127 
    128 	/* shut up compiler warning */
    129 	compile_info = compile_info;
    130 
    131 	err = (*jvmti)->GetMethodDeclaringClass(jvmti, method,
    132 						&declaring_class);
    133 	if (handle_error(err, "GetMethodDeclaringClass()", 1))
    134 		goto cleanup2;
    135 
    136 	if (can_get_line_numbers && map_length && map) {
    137 		jint entry_count;
    138 
    139 		err = (*jvmti)->GetLineNumberTable(jvmti, method,
    140 						   &entry_count, &table_ptr);
    141 		if (err == JVMTI_ERROR_NONE) {
    142 			err = (*jvmti)->GetSourceFileName(jvmti,
    143 				declaring_class, &source_filename);
    144 			if (err ==  JVMTI_ERROR_NONE) {
    145 				debug_line =
    146 					create_debug_line_info(map_length, map,
    147 						entry_count, table_ptr,
    148 						source_filename);
    149 			} else if (err != JVMTI_ERROR_ABSENT_INFORMATION) {
    150 				handle_error(err, "GetSourceFileName()", 1);
    151 			}
    152 		} else if (err != JVMTI_ERROR_NATIVE_METHOD &&
    153 			   err != JVMTI_ERROR_ABSENT_INFORMATION) {
    154 			handle_error(err, "GetLineNumberTable()", 1);
    155 		}
    156 	}
    157 
    158 	err = (*jvmti)->GetClassSignature(jvmti, declaring_class,
    159 					  &class_signature, NULL);
    160 	if (handle_error(err, "GetClassSignature()", 1))
    161 		goto cleanup1;
    162 
    163 	err = (*jvmti)->GetMethodName(jvmti, method, &method_name,
    164 				      &method_signature, NULL);
    165 	if (handle_error(err, "GetMethodName()", 1))
    166 		goto cleanup;
    167 
    168 	if (debug) {
    169 		fprintf(stderr, "load: declaring_class=%p, class=%s, "
    170 			"method=%s, signature=%s, addr=%p, size=%i \n",
    171 			declaring_class, class_signature, method_name,
    172 			method_signature, code_addr, code_size);
    173 	}
    174 
    175 	{
    176 	int cnt = strlen(method_name) + strlen(class_signature) +
    177 		strlen(method_signature) + 2;
    178 	char buf[cnt];
    179 	strncpy(buf, class_signature, cnt - 1);
    180 	strncat(buf, method_name, cnt - strlen(buf) - 1);
    181 	strncat(buf, method_signature, cnt - strlen(buf) - 1);
    182 	if (op_write_native_code(agent_hdl, buf,
    183 				 (uint64_t)(uintptr_t) code_addr,
    184 				 code_addr, code_size)) {
    185 		perror("Error: op_write_native_code()");
    186 		goto cleanup;
    187 	}
    188 	}
    189 
    190 	if (debug_line)
    191 		if (op_write_debug_line_info(agent_hdl, code_addr, map_length,
    192 					     debug_line))
    193 			perror("Error: op_write_debug_line_info()");
    194 
    195 cleanup:
    196 	(*jvmti)->Deallocate(jvmti, (unsigned char *)method_name);
    197 	(*jvmti)->Deallocate(jvmti, (unsigned char *)method_signature);
    198 cleanup1:
    199 	(*jvmti)->Deallocate(jvmti, (unsigned char *)class_signature);
    200 	(*jvmti)->Deallocate(jvmti, (unsigned char *)table_ptr);
    201 	(*jvmti)->Deallocate(jvmti, (unsigned char *)source_filename);
    202 cleanup2:
    203 	free(debug_line);
    204 }
    205 
    206 
    207 static void JNICALL cb_compiled_method_unload(jvmtiEnv * jvmti_env,
    208 	jmethodID method, void const * code_addr)
    209 {
    210 	/* shut up compiler warning */
    211 	jvmti_env = jvmti_env;
    212 	method = method;
    213 
    214 	if (debug)
    215 		fprintf(stderr, "unload: addr=%p\n", code_addr);
    216 	if (op_unload_native_code(agent_hdl, (uint64_t)(uintptr_t) code_addr))
    217 		perror("Error: op_unload_native_code()");
    218 }
    219 
    220 
    221 static void JNICALL cb_dynamic_code_generated(jvmtiEnv * jvmti_env,
    222 	char const * name, void const * code_addr, jint code_size)
    223 {
    224 	/* shut up compiler warning */
    225 	jvmti_env = jvmti_env;
    226 	if (debug) {
    227 		fprintf(stderr, "dyncode: name=%s, addr=%p, size=%i \n",
    228 			name, code_addr, code_size);
    229 	}
    230 	if (op_write_native_code(agent_hdl, name,
    231 				 (uint64_t)(uintptr_t) code_addr,
    232 				 code_addr, code_size))
    233 		perror("Error: op_write_native_code()");
    234 }
    235 
    236 
    237 JNIEXPORT jint JNICALL
    238 Agent_OnLoad(JavaVM * jvm, char * options, void * reserved)
    239 {
    240 	jint rc;
    241 	jvmtiEnv * jvmti = NULL;
    242 	jvmtiEventCallbacks callbacks;
    243 	jvmtiCapabilities caps;
    244 	jvmtiJlocationFormat format;
    245 	jvmtiError error;
    246 
    247 	/* shut up compiler warning */
    248 	reserved = reserved;
    249 
    250 	if (options && !strcmp("version", options)) {
    251 		fprintf(stderr, "jvmti_oprofile: current libopagent version %i.%i.\n",
    252 		        op_major_version(), op_minor_version());
    253 		return -1;
    254 	}
    255 
    256 	if (options && !strcmp("debug", options))
    257 		debug = 1;
    258 
    259 	if (debug)
    260 		fprintf(stderr, "jvmti_oprofile: agent activated\n");
    261 
    262 	agent_hdl = op_open_agent();
    263 	if (!agent_hdl) {
    264 		perror("Error: op_open_agent()");
    265 		return -1;
    266 	}
    267 
    268 	rc = (*jvm)->GetEnv(jvm, (void *)&jvmti, JVMTI_VERSION_1);
    269 	if (rc != JNI_OK) {
    270 		fprintf(stderr, "Error: GetEnv(), rc=%i\n", rc);
    271 		return -1;
    272 	}
    273 
    274 	memset(&caps, '\0', sizeof(caps));
    275 	caps.can_generate_compiled_method_load_events = 1;
    276 	error = (*jvmti)->AddCapabilities(jvmti, &caps);
    277 	if (handle_error(error, "AddCapabilities()", 1))
    278 		return -1;
    279 
    280 	/* FIXME: settable through command line, default on/off? */
    281 	error = (*jvmti)->GetJLocationFormat(jvmti, &format);
    282 	if (!handle_error(error, "GetJLocationFormat", 1) &&
    283 	    format == JVMTI_JLOCATION_JVMBCI) {
    284 		memset(&caps, '\0', sizeof(caps));
    285 		caps.can_get_line_numbers = 1;
    286 		caps.can_get_source_file_name = 1;
    287 		error = (*jvmti)->AddCapabilities(jvmti, &caps);
    288 		if (!handle_error(error, "AddCapabilities()", 1))
    289 			can_get_line_numbers = 1;
    290 	}
    291 
    292 	memset(&callbacks, 0, sizeof(callbacks));
    293 	callbacks.CompiledMethodLoad = cb_compiled_method_load;
    294 	callbacks.CompiledMethodUnload = cb_compiled_method_unload;
    295 	callbacks.DynamicCodeGenerated = cb_dynamic_code_generated;
    296 	error = (*jvmti)->SetEventCallbacks(jvmti, &callbacks,
    297 					    sizeof(callbacks));
    298 	if (handle_error(error, "SetEventCallbacks()", 1))
    299 		return -1;
    300 
    301 	error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
    302 			JVMTI_EVENT_COMPILED_METHOD_LOAD, NULL);
    303 	if (handle_error(error, "SetEventNotificationMode() "
    304 			 "JVMTI_EVENT_COMPILED_METHOD_LOAD", 1))
    305 		return -1;
    306 	error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
    307 			JVMTI_EVENT_COMPILED_METHOD_UNLOAD, NULL);
    308 	if (handle_error(error, "SetEventNotificationMode() "
    309 			 "JVMTI_EVENT_COMPILED_METHOD_UNLOAD", 1))
    310 		return -1;
    311 	error = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
    312 			JVMTI_EVENT_DYNAMIC_CODE_GENERATED, NULL);
    313 	if (handle_error(error, "SetEventNotificationMode() "
    314 			 "JVMTI_EVENT_DYNAMIC_CODE_GENERATED", 1))
    315 		return -1;
    316 	return 0;
    317 }
    318 
    319 
    320 JNIEXPORT void JNICALL Agent_OnUnload(JavaVM * jvm)
    321 {
    322 	/* shut up compiler warning */
    323 	jvm = jvm;
    324 	if (op_close_agent(agent_hdl))
    325 		perror("Error: op_close_agent()");
    326 }
    327