1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/compiler.h>
3 #include <linux/string.h>
10 #include <jvmticmlr.h>
13 #include "jvmti_agent.h"
15 static int has_line_numbers
;
18 static void print_error(jvmtiEnv
*jvmti
, const char *msg
, jvmtiError ret
)
22 err
= (*jvmti
)->GetErrorName(jvmti
, ret
, &err_msg
);
23 if (err
== JVMTI_ERROR_NONE
) {
24 warnx("%s failed with %s", msg
, err_msg
);
25 (*jvmti
)->Deallocate(jvmti
, (unsigned char *)err_msg
);
27 warnx("%s failed with an unknown error %d", msg
, ret
);
32 do_get_line_numbers(jvmtiEnv
*jvmti
, void *pc
, jmethodID m
, jint bci
,
33 jvmti_line_info_t
*tab
, jint
*nr
)
37 jvmtiLineNumberEntry
*loc_tab
= NULL
;
40 ret
= (*jvmti
)->GetLineNumberTable(jvmti
, m
, &nr_lines
, &loc_tab
);
41 if (ret
!= JVMTI_ERROR_NONE
) {
42 print_error(jvmti
, "GetLineNumberTable", ret
);
46 for (i
= 0; i
< nr_lines
; i
++) {
47 if (loc_tab
[i
].start_location
< bci
) {
48 tab
[lines
].pc
= (unsigned long)pc
;
49 tab
[lines
].line_number
= loc_tab
[i
].line_number
;
50 tab
[lines
].discrim
= 0; /* not yet used */
51 tab
[lines
].methodID
= m
;
57 (*jvmti
)->Deallocate(jvmti
, (unsigned char *)loc_tab
);
59 return JVMTI_ERROR_NONE
;
63 get_line_numbers(jvmtiEnv
*jvmti
, const void *compile_info
, jvmti_line_info_t
**tab
, int *nr_lines
)
65 const jvmtiCompiledMethodLoadRecordHeader
*hdr
;
66 jvmtiCompiledMethodLoadInlineRecord
*rec
;
67 jvmtiLineNumberEntry
*lne
= NULL
;
71 int i
, lines_total
= 0;
73 if (!(tab
&& nr_lines
))
74 return JVMTI_ERROR_NULL_POINTER
;
77 * Phase 1 -- get the number of lines necessary
79 for (hdr
= compile_info
; hdr
!= NULL
; hdr
= hdr
->next
) {
80 if (hdr
->kind
== JVMTI_CMLR_INLINE_INFO
) {
81 rec
= (jvmtiCompiledMethodLoadInlineRecord
*)hdr
;
82 for (i
= 0; i
< rec
->numpcs
; i
++) {
86 * unfortunately, need a tab to get the number of lines!
88 ret
= (*jvmti
)->GetLineNumberTable(jvmti
, c
->methods
[0], &nr
, &lne
);
89 if (ret
== JVMTI_ERROR_NONE
) {
90 /* free what was allocated for nothing */
91 (*jvmti
)->Deallocate(jvmti
, (unsigned char *)lne
);
94 print_error(jvmti
, "GetLineNumberTable", ret
);
101 return JVMTI_ERROR_NOT_FOUND
;
104 * Phase 2 -- allocate big enough line table
106 *tab
= malloc(nr_total
* sizeof(**tab
));
108 return JVMTI_ERROR_OUT_OF_MEMORY
;
110 for (hdr
= compile_info
; hdr
!= NULL
; hdr
= hdr
->next
) {
111 if (hdr
->kind
== JVMTI_CMLR_INLINE_INFO
) {
112 rec
= (jvmtiCompiledMethodLoadInlineRecord
*)hdr
;
113 for (i
= 0; i
< rec
->numpcs
; i
++) {
116 ret
= do_get_line_numbers(jvmti
, c
->pc
,
121 if (ret
== JVMTI_ERROR_NONE
)
126 *nr_lines
= lines_total
;
127 return JVMTI_ERROR_NONE
;
131 copy_class_filename(const char * class_sign
, const char * file_name
, char * result
, size_t max_length
)
134 * Assume path name is class hierarchy, this is a common practice with Java programs
136 if (*class_sign
== 'L') {
138 char *p
= strrchr(class_sign
, '/');
140 /* drop the 'L' prefix and copy up to the final '/' */
141 for (i
= 0; i
< (p
- class_sign
); i
++)
142 result
[i
] = class_sign
[i
+1];
145 * append file name, we use loops and not string ops to avoid modifying
146 * class_sign which is used later for the symbol name
148 for (j
= 0; i
< (max_length
- 1) && file_name
&& j
< strlen(file_name
); j
++, i
++)
149 result
[i
] = file_name
[j
];
154 strlcpy(result
, file_name
, max_length
);
159 get_source_filename(jvmtiEnv
*jvmti
, jmethodID methodID
, char ** buffer
)
163 char *file_name
= NULL
;
164 char *class_sign
= NULL
;
168 ret
= (*jvmti
)->GetMethodDeclaringClass(jvmti
, methodID
, &decl_class
);
169 if (ret
!= JVMTI_ERROR_NONE
) {
170 print_error(jvmti
, "GetMethodDeclaringClass", ret
);
174 ret
= (*jvmti
)->GetSourceFileName(jvmti
, decl_class
, &file_name
);
175 if (ret
!= JVMTI_ERROR_NONE
) {
176 print_error(jvmti
, "GetSourceFileName", ret
);
180 ret
= (*jvmti
)->GetClassSignature(jvmti
, decl_class
, &class_sign
, NULL
);
181 if (ret
!= JVMTI_ERROR_NONE
) {
182 print_error(jvmti
, "GetClassSignature", ret
);
183 goto free_file_name_error
;
186 copy_class_filename(class_sign
, file_name
, fn
, PATH_MAX
);
188 *buffer
= malloc((len
+ 1) * sizeof(char));
190 print_error(jvmti
, "GetClassSignature", ret
);
191 ret
= JVMTI_ERROR_OUT_OF_MEMORY
;
192 goto free_class_sign_error
;
195 ret
= JVMTI_ERROR_NONE
;
197 free_class_sign_error
:
198 (*jvmti
)->Deallocate(jvmti
, (unsigned char *)class_sign
);
199 free_file_name_error
:
200 (*jvmti
)->Deallocate(jvmti
, (unsigned char *)file_name
);
206 fill_source_filenames(jvmtiEnv
*jvmti
, int nr_lines
,
207 const jvmti_line_info_t
* line_tab
,
213 for (index
= 0; index
< nr_lines
; ++index
) {
214 ret
= get_source_filename(jvmti
, line_tab
[index
].methodID
, &(file_names
[index
]));
215 if (ret
!= JVMTI_ERROR_NONE
)
219 return JVMTI_ERROR_NONE
;
223 compiled_method_load_cb(jvmtiEnv
*jvmti
,
226 void const *code_addr
,
228 jvmtiAddrLocationMap
const *map
,
229 const void *compile_info
)
231 jvmti_line_info_t
*line_tab
= NULL
;
232 char ** line_file_names
= NULL
;
234 char *class_sign
= NULL
;
235 char *func_name
= NULL
;
236 char *func_sign
= NULL
;
237 char *file_name
= NULL
;
239 uint64_t addr
= (uint64_t)(uintptr_t)code_addr
;
241 int nr_lines
= 0; /* in line_tab[] */
243 int output_debug_info
= 0;
245 ret
= (*jvmti
)->GetMethodDeclaringClass(jvmti
, method
,
247 if (ret
!= JVMTI_ERROR_NONE
) {
248 print_error(jvmti
, "GetMethodDeclaringClass", ret
);
252 if (has_line_numbers
&& map
&& map_length
) {
253 ret
= get_line_numbers(jvmti
, compile_info
, &line_tab
, &nr_lines
);
254 if (ret
!= JVMTI_ERROR_NONE
) {
255 warnx("jvmti: cannot get line table for method");
257 } else if (nr_lines
> 0) {
258 line_file_names
= malloc(sizeof(char*) * nr_lines
);
259 if (!line_file_names
) {
260 warnx("jvmti: cannot allocate space for line table method names");
262 memset(line_file_names
, 0, sizeof(char*) * nr_lines
);
263 ret
= fill_source_filenames(jvmti
, nr_lines
, line_tab
, line_file_names
);
264 if (ret
!= JVMTI_ERROR_NONE
) {
265 warnx("jvmti: fill_source_filenames failed");
267 output_debug_info
= 1;
273 ret
= (*jvmti
)->GetSourceFileName(jvmti
, decl_class
, &file_name
);
274 if (ret
!= JVMTI_ERROR_NONE
) {
275 print_error(jvmti
, "GetSourceFileName", ret
);
279 ret
= (*jvmti
)->GetClassSignature(jvmti
, decl_class
,
281 if (ret
!= JVMTI_ERROR_NONE
) {
282 print_error(jvmti
, "GetClassSignature", ret
);
286 ret
= (*jvmti
)->GetMethodName(jvmti
, method
, &func_name
,
288 if (ret
!= JVMTI_ERROR_NONE
) {
289 print_error(jvmti
, "GetMethodName", ret
);
293 copy_class_filename(class_sign
, file_name
, fn
, PATH_MAX
);
296 * write source line info record if we have it
298 if (output_debug_info
)
299 if (jvmti_write_debug_info(jvmti_agent
, addr
, nr_lines
, line_tab
, (const char * const *) line_file_names
))
300 warnx("jvmti: write_debug_info() failed");
302 len
= strlen(func_name
) + strlen(class_sign
) + strlen(func_sign
) + 2;
305 snprintf(str
, len
, "%s%s%s", class_sign
, func_name
, func_sign
);
307 if (jvmti_write_code(jvmti_agent
, str
, addr
, code_addr
, code_size
))
308 warnx("jvmti: write_code() failed");
311 (*jvmti
)->Deallocate(jvmti
, (unsigned char *)func_name
);
312 (*jvmti
)->Deallocate(jvmti
, (unsigned char *)func_sign
);
313 (*jvmti
)->Deallocate(jvmti
, (unsigned char *)class_sign
);
314 (*jvmti
)->Deallocate(jvmti
, (unsigned char *)file_name
);
316 while (line_file_names
&& (nr_lines
> 0)) {
317 if (line_file_names
[nr_lines
- 1]) {
318 free(line_file_names
[nr_lines
- 1]);
322 free(line_file_names
);
326 code_generated_cb(jvmtiEnv
*jvmti
,
328 void const *code_addr
,
331 uint64_t addr
= (uint64_t)(unsigned long)code_addr
;
334 ret
= jvmti_write_code(jvmti_agent
, name
, addr
, code_addr
, code_size
);
336 warnx("jvmti: write_code() failed for code_generated");
339 JNIEXPORT jint JNICALL
340 Agent_OnLoad(JavaVM
*jvm
, char *options
, void *reserved __maybe_unused
)
342 jvmtiEventCallbacks cb
;
343 jvmtiCapabilities caps1
;
344 jvmtiJlocationFormat format
;
345 jvmtiEnv
*jvmti
= NULL
;
348 jvmti_agent
= jvmti_open();
350 warnx("jvmti: open_agent failed");
355 * Request a JVMTI interface version 1 environment
357 ret
= (*jvm
)->GetEnv(jvm
, (void *)&jvmti
, JVMTI_VERSION_1
);
359 warnx("jvmti: jvmti version 1 not supported");
364 * acquire method_load capability, we require it
365 * request line numbers (optional)
367 memset(&caps1
, 0, sizeof(caps1
));
368 caps1
.can_generate_compiled_method_load_events
= 1;
370 ret
= (*jvmti
)->AddCapabilities(jvmti
, &caps1
);
371 if (ret
!= JVMTI_ERROR_NONE
) {
372 print_error(jvmti
, "AddCapabilities", ret
);
375 ret
= (*jvmti
)->GetJLocationFormat(jvmti
, &format
);
376 if (ret
== JVMTI_ERROR_NONE
&& format
== JVMTI_JLOCATION_JVMBCI
) {
377 memset(&caps1
, 0, sizeof(caps1
));
378 caps1
.can_get_line_numbers
= 1;
379 caps1
.can_get_source_file_name
= 1;
380 ret
= (*jvmti
)->AddCapabilities(jvmti
, &caps1
);
381 if (ret
== JVMTI_ERROR_NONE
)
382 has_line_numbers
= 1;
383 } else if (ret
!= JVMTI_ERROR_NONE
)
384 print_error(jvmti
, "GetJLocationFormat", ret
);
387 memset(&cb
, 0, sizeof(cb
));
389 cb
.CompiledMethodLoad
= compiled_method_load_cb
;
390 cb
.DynamicCodeGenerated
= code_generated_cb
;
392 ret
= (*jvmti
)->SetEventCallbacks(jvmti
, &cb
, sizeof(cb
));
393 if (ret
!= JVMTI_ERROR_NONE
) {
394 print_error(jvmti
, "SetEventCallbacks", ret
);
398 ret
= (*jvmti
)->SetEventNotificationMode(jvmti
, JVMTI_ENABLE
,
399 JVMTI_EVENT_COMPILED_METHOD_LOAD
, NULL
);
400 if (ret
!= JVMTI_ERROR_NONE
) {
401 print_error(jvmti
, "SetEventNotificationMode(METHOD_LOAD)", ret
);
405 ret
= (*jvmti
)->SetEventNotificationMode(jvmti
, JVMTI_ENABLE
,
406 JVMTI_EVENT_DYNAMIC_CODE_GENERATED
, NULL
);
407 if (ret
!= JVMTI_ERROR_NONE
) {
408 print_error(jvmti
, "SetEventNotificationMode(CODE_GENERATED)", ret
);
414 JNIEXPORT
void JNICALL
415 Agent_OnUnload(JavaVM
*jvm __maybe_unused
)
419 ret
= jvmti_close(jvmti_agent
);
421 errx(1, "Error: op_close_agent()");