• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
handle_error(jvmtiError err,char const * msg,int severe)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 *
create_debug_line_info(jint map_length,jvmtiAddrLocationMap const * map,jint entry_count,jvmtiLineNumberEntry * table_ptr,char const * source_filename)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 
cb_compiled_method_load(jvmtiEnv * jvmti,jmethodID method,jint code_size,void const * code_addr,jint map_length,jvmtiAddrLocationMap const * map,void const * compile_info)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 
cb_compiled_method_unload(jvmtiEnv * jvmti_env,jmethodID method,void const * code_addr)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 
cb_dynamic_code_generated(jvmtiEnv * jvmti_env,char const * name,void const * code_addr,jint code_size)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
Agent_OnLoad(JavaVM * jvm,char * options,void * reserved)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 
Agent_OnUnload(JavaVM * jvm)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