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