xref: /openbmc/linux/tools/perf/jvmti/libjvmti.c (revision d774a589)
1 #include <sys/types.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <stdlib.h>
5 #include <err.h>
6 #include <jvmti.h>
7 #include <jvmticmlr.h>
8 #include <limits.h>
9 
10 #include "jvmti_agent.h"
11 
12 static int has_line_numbers;
13 void *jvmti_agent;
14 
15 static void print_error(jvmtiEnv *jvmti, const char *msg, jvmtiError ret)
16 {
17 	char *err_msg = NULL;
18 	jvmtiError err;
19 	err = (*jvmti)->GetErrorName(jvmti, ret, &err_msg);
20 	if (err == JVMTI_ERROR_NONE) {
21 		warnx("%s failed with %s", msg, err_msg);
22 		(*jvmti)->Deallocate(jvmti, (unsigned char *)err_msg);
23 	} else {
24 		warnx("%s failed with an unknown error %d", msg, ret);
25 	}
26 }
27 
28 static jvmtiError
29 do_get_line_numbers(jvmtiEnv *jvmti, void *pc, jmethodID m, jint bci,
30 		    jvmti_line_info_t *tab, jint *nr)
31 {
32 	jint i, lines = 0;
33 	jint nr_lines = 0;
34 	jvmtiLineNumberEntry *loc_tab = NULL;
35 	jvmtiError ret;
36 
37 	ret = (*jvmti)->GetLineNumberTable(jvmti, m, &nr_lines, &loc_tab);
38 	if (ret != JVMTI_ERROR_NONE) {
39 		print_error(jvmti, "GetLineNumberTable", ret);
40 		return ret;
41 	}
42 
43 	for (i = 0; i < nr_lines; i++) {
44 		if (loc_tab[i].start_location < bci) {
45 			tab[lines].pc = (unsigned long)pc;
46 			tab[lines].line_number = loc_tab[i].line_number;
47 			tab[lines].discrim = 0; /* not yet used */
48 			lines++;
49 		} else {
50 			break;
51 		}
52 	}
53 	(*jvmti)->Deallocate(jvmti, (unsigned char *)loc_tab);
54 	*nr = lines;
55 	return JVMTI_ERROR_NONE;
56 }
57 
58 static jvmtiError
59 get_line_numbers(jvmtiEnv *jvmti, const void *compile_info, jvmti_line_info_t **tab, int *nr_lines)
60 {
61 	const jvmtiCompiledMethodLoadRecordHeader *hdr;
62 	jvmtiCompiledMethodLoadInlineRecord *rec;
63 	jvmtiLineNumberEntry *lne = NULL;
64 	PCStackInfo *c;
65 	jint nr, ret;
66 	int nr_total = 0;
67 	int i, lines_total = 0;
68 
69 	if (!(tab && nr_lines))
70 		return JVMTI_ERROR_NULL_POINTER;
71 
72 	/*
73 	 * Phase 1 -- get the number of lines necessary
74 	 */
75 	for (hdr = compile_info; hdr != NULL; hdr = hdr->next) {
76 		if (hdr->kind == JVMTI_CMLR_INLINE_INFO) {
77 			rec = (jvmtiCompiledMethodLoadInlineRecord *)hdr;
78 			for (i = 0; i < rec->numpcs; i++) {
79 				c = rec->pcinfo + i;
80 				nr = 0;
81 				/*
82 				 * unfortunately, need a tab to get the number of lines!
83 				 */
84 				ret = (*jvmti)->GetLineNumberTable(jvmti, c->methods[0], &nr, &lne);
85 				if (ret == JVMTI_ERROR_NONE) {
86 					/* free what was allocated for nothing */
87 					(*jvmti)->Deallocate(jvmti, (unsigned char *)lne);
88 					nr_total += (int)nr;
89 				} else {
90 					print_error(jvmti, "GetLineNumberTable", ret);
91 				}
92 			}
93 		}
94 	}
95 
96 	if (nr_total == 0)
97 		return JVMTI_ERROR_NOT_FOUND;
98 
99 	/*
100 	 * Phase 2 -- allocate big enough line table
101 	 */
102 	*tab = malloc(nr_total * sizeof(**tab));
103 	if (!*tab)
104 		return JVMTI_ERROR_OUT_OF_MEMORY;
105 
106 	for (hdr = compile_info; hdr != NULL; hdr = hdr->next) {
107 		if (hdr->kind == JVMTI_CMLR_INLINE_INFO) {
108 			rec = (jvmtiCompiledMethodLoadInlineRecord *)hdr;
109 			for (i = 0; i < rec->numpcs; i++) {
110 				c = rec->pcinfo + i;
111 				nr = 0;
112 				ret = do_get_line_numbers(jvmti, c->pc,
113 							  c->methods[0],
114 							  c->bcis[0],
115 							  *tab + lines_total,
116 							  &nr);
117 				if (ret == JVMTI_ERROR_NONE)
118 					lines_total += nr;
119 			}
120 		}
121 	}
122 	*nr_lines = lines_total;
123 	return JVMTI_ERROR_NONE;
124 }
125 
126 static void JNICALL
127 compiled_method_load_cb(jvmtiEnv *jvmti,
128 			jmethodID method,
129 			jint code_size,
130 			void const *code_addr,
131 			jint map_length,
132 			jvmtiAddrLocationMap const *map,
133 			const void *compile_info)
134 {
135 	jvmti_line_info_t *line_tab = NULL;
136 	jclass decl_class;
137 	char *class_sign = NULL;
138 	char *func_name = NULL;
139 	char *func_sign = NULL;
140 	char *file_name= NULL;
141 	char fn[PATH_MAX];
142 	uint64_t addr = (uint64_t)(uintptr_t)code_addr;
143 	jvmtiError ret;
144 	int nr_lines = 0; /* in line_tab[] */
145 	size_t len;
146 
147 	ret = (*jvmti)->GetMethodDeclaringClass(jvmti, method,
148 						&decl_class);
149 	if (ret != JVMTI_ERROR_NONE) {
150 		print_error(jvmti, "GetMethodDeclaringClass", ret);
151 		return;
152 	}
153 
154 	if (has_line_numbers && map && map_length) {
155 		ret = get_line_numbers(jvmti, compile_info, &line_tab, &nr_lines);
156 		if (ret != JVMTI_ERROR_NONE) {
157 			warnx("jvmti: cannot get line table for method");
158 			nr_lines = 0;
159 		}
160 	}
161 
162 	ret = (*jvmti)->GetSourceFileName(jvmti, decl_class, &file_name);
163 	if (ret != JVMTI_ERROR_NONE) {
164 		print_error(jvmti, "GetSourceFileName", ret);
165 		goto error;
166 	}
167 
168 	ret = (*jvmti)->GetClassSignature(jvmti, decl_class,
169 					  &class_sign, NULL);
170 	if (ret != JVMTI_ERROR_NONE) {
171 		print_error(jvmti, "GetClassSignature", ret);
172 		goto error;
173 	}
174 
175 	ret = (*jvmti)->GetMethodName(jvmti, method, &func_name,
176 				      &func_sign, NULL);
177 	if (ret != JVMTI_ERROR_NONE) {
178 		print_error(jvmti, "GetMethodName", ret);
179 		goto error;
180 	}
181 
182 	/*
183 	 * Assume path name is class hierarchy, this is a common practice with Java programs
184 	 */
185 	if (*class_sign == 'L') {
186 		int j, i = 0;
187 		char *p = strrchr(class_sign, '/');
188 		if (p) {
189 			/* drop the 'L' prefix and copy up to the final '/' */
190 			for (i = 0; i < (p - class_sign); i++)
191 				fn[i] = class_sign[i+1];
192 		}
193 		/*
194 		 * append file name, we use loops and not string ops to avoid modifying
195 		 * class_sign which is used later for the symbol name
196 		 */
197 		for (j = 0; i < (PATH_MAX - 1) && file_name && j < strlen(file_name); j++, i++)
198 			fn[i] = file_name[j];
199 		fn[i] = '\0';
200 	} else {
201 		/* fallback case */
202 		strcpy(fn, file_name);
203 	}
204 	/*
205 	 * write source line info record if we have it
206 	 */
207 	if (jvmti_write_debug_info(jvmti_agent, addr, fn, line_tab, nr_lines))
208 		warnx("jvmti: write_debug_info() failed");
209 
210 	len = strlen(func_name) + strlen(class_sign) + strlen(func_sign) + 2;
211 	{
212 		char str[len];
213 		snprintf(str, len, "%s%s%s", class_sign, func_name, func_sign);
214 
215 		if (jvmti_write_code(jvmti_agent, str, addr, code_addr, code_size))
216 			warnx("jvmti: write_code() failed");
217 	}
218 error:
219 	(*jvmti)->Deallocate(jvmti, (unsigned char *)func_name);
220 	(*jvmti)->Deallocate(jvmti, (unsigned char *)func_sign);
221 	(*jvmti)->Deallocate(jvmti, (unsigned char *)class_sign);
222 	(*jvmti)->Deallocate(jvmti, (unsigned char *)file_name);
223 	free(line_tab);
224 }
225 
226 static void JNICALL
227 code_generated_cb(jvmtiEnv *jvmti,
228 		  char const *name,
229 		  void const *code_addr,
230 		  jint code_size)
231 {
232 	uint64_t addr = (uint64_t)(unsigned long)code_addr;
233 	int ret;
234 
235 	ret = jvmti_write_code(jvmti_agent, name, addr, code_addr, code_size);
236 	if (ret)
237 		warnx("jvmti: write_code() failed for code_generated");
238 }
239 
240 JNIEXPORT jint JNICALL
241 Agent_OnLoad(JavaVM *jvm, char *options, void *reserved __unused)
242 {
243 	jvmtiEventCallbacks cb;
244 	jvmtiCapabilities caps1;
245 	jvmtiJlocationFormat format;
246 	jvmtiEnv *jvmti = NULL;
247 	jint ret;
248 
249 	jvmti_agent = jvmti_open();
250 	if (!jvmti_agent) {
251 		warnx("jvmti: open_agent failed");
252 		return -1;
253 	}
254 
255 	/*
256 	 * Request a JVMTI interface version 1 environment
257 	 */
258 	ret = (*jvm)->GetEnv(jvm, (void *)&jvmti, JVMTI_VERSION_1);
259 	if (ret != JNI_OK) {
260 		warnx("jvmti: jvmti version 1 not supported");
261 		return -1;
262 	}
263 
264 	/*
265 	 * acquire method_load capability, we require it
266 	 * request line numbers (optional)
267 	 */
268 	memset(&caps1, 0, sizeof(caps1));
269 	caps1.can_generate_compiled_method_load_events = 1;
270 
271 	ret = (*jvmti)->AddCapabilities(jvmti, &caps1);
272 	if (ret != JVMTI_ERROR_NONE) {
273 		print_error(jvmti, "AddCapabilities", ret);
274 		return -1;
275 	}
276 	ret = (*jvmti)->GetJLocationFormat(jvmti, &format);
277         if (ret == JVMTI_ERROR_NONE && format == JVMTI_JLOCATION_JVMBCI) {
278                 memset(&caps1, 0, sizeof(caps1));
279                 caps1.can_get_line_numbers = 1;
280                 caps1.can_get_source_file_name = 1;
281 		ret = (*jvmti)->AddCapabilities(jvmti, &caps1);
282                 if (ret == JVMTI_ERROR_NONE)
283                         has_line_numbers = 1;
284         } else if (ret != JVMTI_ERROR_NONE)
285 		print_error(jvmti, "GetJLocationFormat", ret);
286 
287 
288 	memset(&cb, 0, sizeof(cb));
289 
290 	cb.CompiledMethodLoad   = compiled_method_load_cb;
291 	cb.DynamicCodeGenerated = code_generated_cb;
292 
293 	ret = (*jvmti)->SetEventCallbacks(jvmti, &cb, sizeof(cb));
294 	if (ret != JVMTI_ERROR_NONE) {
295 		print_error(jvmti, "SetEventCallbacks", ret);
296 		return -1;
297 	}
298 
299 	ret = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
300 			JVMTI_EVENT_COMPILED_METHOD_LOAD, NULL);
301 	if (ret != JVMTI_ERROR_NONE) {
302 		print_error(jvmti, "SetEventNotificationMode(METHOD_LOAD)", ret);
303 		return -1;
304 	}
305 
306 	ret = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
307 			JVMTI_EVENT_DYNAMIC_CODE_GENERATED, NULL);
308 	if (ret != JVMTI_ERROR_NONE) {
309 		print_error(jvmti, "SetEventNotificationMode(CODE_GENERATED)", ret);
310 		return -1;
311 	}
312 	return 0;
313 }
314 
315 JNIEXPORT void JNICALL
316 Agent_OnUnload(JavaVM *jvm __unused)
317 {
318 	int ret;
319 
320 	ret = jvmti_close(jvmti_agent);
321 	if (ret)
322 		errx(1, "Error: op_close_agent()");
323 }
324