libjvmti_oprofile.c revision 8cfa702f803c5ef6a2b062a489a1b2cf66b45b5e
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
37static int debug = 0;
38static int can_get_line_numbers = 0;
39static 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 */
45static 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 */
61static struct debug_line_info *
62create_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
114static 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
195cleanup:
196	(*jvmti)->Deallocate(jvmti, (unsigned char *)method_name);
197	(*jvmti)->Deallocate(jvmti, (unsigned char *)method_signature);
198cleanup1:
199	(*jvmti)->Deallocate(jvmti, (unsigned char *)class_signature);
200	(*jvmti)->Deallocate(jvmti, (unsigned char *)table_ptr);
201	(*jvmti)->Deallocate(jvmti, (unsigned char *)source_filename);
202cleanup2:
203	free(debug_line);
204}
205
206
207static 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
221static 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
237JNIEXPORT jint JNICALL
238Agent_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
320JNIEXPORT 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