1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/compiler.h>
10 #include <jvmticmlr.h>
14 #include "jvmti_agent.h"
16 static int has_line_numbers
;
19 static void print_error(jvmtiEnv
*jvmti
, const char *msg
, jvmtiError ret
)
23 err
= (*jvmti
)->GetErrorName(jvmti
, ret
, &err_msg
);
24 if (err
== JVMTI_ERROR_NONE
) {
25 warnx("%s failed with %s", msg
, err_msg
);
26 (*jvmti
)->Deallocate(jvmti
, (unsigned char *)err_msg
);
28 warnx("%s failed with an unknown error %d", msg
, ret
);
32 #ifdef HAVE_JVMTI_CMLR
34 do_get_line_numbers(jvmtiEnv
*jvmti
, void *pc
, jmethodID m
, jint bci
,
35 jvmti_line_info_t
*tab
, jint
*nr
)
39 jvmtiLineNumberEntry
*loc_tab
= NULL
;
42 ret
= (*jvmti
)->GetLineNumberTable(jvmti
, m
, &nr_lines
, &loc_tab
);
43 if (ret
!= JVMTI_ERROR_NONE
) {
44 print_error(jvmti
, "GetLineNumberTable", ret
);
48 for (i
= 0; i
< nr_lines
; i
++) {
49 if (loc_tab
[i
].start_location
< bci
) {
50 tab
[lines
].pc
= (unsigned long)pc
;
51 tab
[lines
].line_number
= loc_tab
[i
].line_number
;
52 tab
[lines
].discrim
= 0; /* not yet used */
53 tab
[lines
].methodID
= m
;
59 (*jvmti
)->Deallocate(jvmti
, (unsigned char *)loc_tab
);
61 return JVMTI_ERROR_NONE
;
65 get_line_numbers(jvmtiEnv
*jvmti
, const void *compile_info
, jvmti_line_info_t
**tab
, int *nr_lines
)
67 const jvmtiCompiledMethodLoadRecordHeader
*hdr
;
68 jvmtiCompiledMethodLoadInlineRecord
*rec
;
69 jvmtiLineNumberEntry
*lne
= NULL
;
73 int i
, lines_total
= 0;
75 if (!(tab
&& nr_lines
))
76 return JVMTI_ERROR_NULL_POINTER
;
79 * Phase 1 -- get the number of lines necessary
81 for (hdr
= compile_info
; hdr
!= NULL
; hdr
= hdr
->next
) {
82 if (hdr
->kind
== JVMTI_CMLR_INLINE_INFO
) {
83 rec
= (jvmtiCompiledMethodLoadInlineRecord
*)hdr
;
84 for (i
= 0; i
< rec
->numpcs
; i
++) {
88 * unfortunately, need a tab to get the number of lines!
90 ret
= (*jvmti
)->GetLineNumberTable(jvmti
, c
->methods
[0], &nr
, &lne
);
91 if (ret
== JVMTI_ERROR_NONE
) {
92 /* free what was allocated for nothing */
93 (*jvmti
)->Deallocate(jvmti
, (unsigned char *)lne
);
96 print_error(jvmti
, "GetLineNumberTable", ret
);
103 return JVMTI_ERROR_NOT_FOUND
;
106 * Phase 2 -- allocate big enough line table
108 *tab
= malloc(nr_total
* sizeof(**tab
));
110 return JVMTI_ERROR_OUT_OF_MEMORY
;
112 for (hdr
= compile_info
; hdr
!= NULL
; hdr
= hdr
->next
) {
113 if (hdr
->kind
== JVMTI_CMLR_INLINE_INFO
) {
114 rec
= (jvmtiCompiledMethodLoadInlineRecord
*)hdr
;
115 for (i
= 0; i
< rec
->numpcs
; i
++) {
118 ret
= do_get_line_numbers(jvmti
, c
->pc
,
123 if (ret
== JVMTI_ERROR_NONE
)
128 *nr_lines
= lines_total
;
129 return JVMTI_ERROR_NONE
;
131 #else /* HAVE_JVMTI_CMLR */
134 get_line_numbers(jvmtiEnv
*jvmti __maybe_unused
, const void *compile_info __maybe_unused
,
135 jvmti_line_info_t
**tab __maybe_unused
, int *nr_lines __maybe_unused
)
137 return JVMTI_ERROR_NONE
;
139 #endif /* HAVE_JVMTI_CMLR */
142 copy_class_filename(const char * class_sign
, const char * file_name
, char * result
, size_t max_length
)
145 * Assume path name is class hierarchy, this is a common practice with Java programs
147 if (*class_sign
== 'L') {
149 char *p
= strrchr(class_sign
, '/');
151 /* drop the 'L' prefix and copy up to the final '/' */
152 for (i
= 0; i
< (p
- class_sign
); i
++)
153 result
[i
] = class_sign
[i
+1];
156 * append file name, we use loops and not string ops to avoid modifying
157 * class_sign which is used later for the symbol name
159 for (j
= 0; i
< (max_length
- 1) && file_name
&& j
< strlen(file_name
); j
++, i
++)
160 result
[i
] = file_name
[j
];
165 size_t file_name_len
= strlen(file_name
);
166 strncpy(result
, file_name
, file_name_len
< max_length
? file_name_len
: max_length
);
171 get_source_filename(jvmtiEnv
*jvmti
, jmethodID methodID
, char ** buffer
)
175 char *file_name
= NULL
;
176 char *class_sign
= NULL
;
180 ret
= (*jvmti
)->GetMethodDeclaringClass(jvmti
, methodID
, &decl_class
);
181 if (ret
!= JVMTI_ERROR_NONE
) {
182 print_error(jvmti
, "GetMethodDeclaringClass", ret
);
186 ret
= (*jvmti
)->GetSourceFileName(jvmti
, decl_class
, &file_name
);
187 if (ret
!= JVMTI_ERROR_NONE
) {
188 print_error(jvmti
, "GetSourceFileName", ret
);
192 ret
= (*jvmti
)->GetClassSignature(jvmti
, decl_class
, &class_sign
, NULL
);
193 if (ret
!= JVMTI_ERROR_NONE
) {
194 print_error(jvmti
, "GetClassSignature", ret
);
195 goto free_file_name_error
;
198 copy_class_filename(class_sign
, file_name
, fn
, PATH_MAX
);
200 *buffer
= malloc((len
+ 1) * sizeof(char));
202 print_error(jvmti
, "GetClassSignature", ret
);
203 ret
= JVMTI_ERROR_OUT_OF_MEMORY
;
204 goto free_class_sign_error
;
207 ret
= JVMTI_ERROR_NONE
;
209 free_class_sign_error
:
210 (*jvmti
)->Deallocate(jvmti
, (unsigned char *)class_sign
);
211 free_file_name_error
:
212 (*jvmti
)->Deallocate(jvmti
, (unsigned char *)file_name
);
218 fill_source_filenames(jvmtiEnv
*jvmti
, int nr_lines
,
219 const jvmti_line_info_t
* line_tab
,
225 for (index
= 0; index
< nr_lines
; ++index
) {
226 ret
= get_source_filename(jvmti
, line_tab
[index
].methodID
, &(file_names
[index
]));
227 if (ret
!= JVMTI_ERROR_NONE
)
231 return JVMTI_ERROR_NONE
;
235 compiled_method_load_cb(jvmtiEnv
*jvmti
,
238 void const *code_addr
,
240 jvmtiAddrLocationMap
const *map
,
241 const void *compile_info
)
243 jvmti_line_info_t
*line_tab
= NULL
;
244 char ** line_file_names
= NULL
;
246 char *class_sign
= NULL
;
247 char *func_name
= NULL
;
248 char *func_sign
= NULL
;
249 char *file_name
= NULL
;
251 uint64_t addr
= (uint64_t)(uintptr_t)code_addr
;
253 int nr_lines
= 0; /* in line_tab[] */
255 int output_debug_info
= 0;
257 ret
= (*jvmti
)->GetMethodDeclaringClass(jvmti
, method
,
259 if (ret
!= JVMTI_ERROR_NONE
) {
260 print_error(jvmti
, "GetMethodDeclaringClass", ret
);
264 if (has_line_numbers
&& map
&& map_length
) {
265 ret
= get_line_numbers(jvmti
, compile_info
, &line_tab
, &nr_lines
);
266 if (ret
!= JVMTI_ERROR_NONE
) {
267 warnx("jvmti: cannot get line table for method");
269 } else if (nr_lines
> 0) {
270 line_file_names
= malloc(sizeof(char*) * nr_lines
);
271 if (!line_file_names
) {
272 warnx("jvmti: cannot allocate space for line table method names");
274 memset(line_file_names
, 0, sizeof(char*) * nr_lines
);
275 ret
= fill_source_filenames(jvmti
, nr_lines
, line_tab
, line_file_names
);
276 if (ret
!= JVMTI_ERROR_NONE
) {
277 warnx("jvmti: fill_source_filenames failed");
279 output_debug_info
= 1;
285 ret
= (*jvmti
)->GetSourceFileName(jvmti
, decl_class
, &file_name
);
286 if (ret
!= JVMTI_ERROR_NONE
) {
287 print_error(jvmti
, "GetSourceFileName", ret
);
291 ret
= (*jvmti
)->GetClassSignature(jvmti
, decl_class
,
293 if (ret
!= JVMTI_ERROR_NONE
) {
294 print_error(jvmti
, "GetClassSignature", ret
);
298 ret
= (*jvmti
)->GetMethodName(jvmti
, method
, &func_name
,
300 if (ret
!= JVMTI_ERROR_NONE
) {
301 print_error(jvmti
, "GetMethodName", ret
);
305 copy_class_filename(class_sign
, file_name
, fn
, PATH_MAX
);
308 * write source line info record if we have it
310 if (output_debug_info
)
311 if (jvmti_write_debug_info(jvmti_agent
, addr
, nr_lines
, line_tab
, (const char * const *) line_file_names
))
312 warnx("jvmti: write_debug_info() failed");
314 len
= strlen(func_name
) + strlen(class_sign
) + strlen(func_sign
) + 2;
317 snprintf(str
, len
, "%s%s%s", class_sign
, func_name
, func_sign
);
319 if (jvmti_write_code(jvmti_agent
, str
, addr
, code_addr
, code_size
))
320 warnx("jvmti: write_code() failed");
323 (*jvmti
)->Deallocate(jvmti
, (unsigned char *)func_name
);
324 (*jvmti
)->Deallocate(jvmti
, (unsigned char *)func_sign
);
325 (*jvmti
)->Deallocate(jvmti
, (unsigned char *)class_sign
);
326 (*jvmti
)->Deallocate(jvmti
, (unsigned char *)file_name
);
328 while (line_file_names
&& (nr_lines
> 0)) {
329 if (line_file_names
[nr_lines
- 1]) {
330 free(line_file_names
[nr_lines
- 1]);
334 free(line_file_names
);
338 code_generated_cb(jvmtiEnv
*jvmti
,
340 void const *code_addr
,
343 uint64_t addr
= (uint64_t)(unsigned long)code_addr
;
346 ret
= jvmti_write_code(jvmti_agent
, name
, addr
, code_addr
, code_size
);
348 warnx("jvmti: write_code() failed for code_generated");
351 JNIEXPORT jint JNICALL
352 Agent_OnLoad(JavaVM
*jvm
, char *options
, void *reserved __maybe_unused
)
354 jvmtiEventCallbacks cb
;
355 jvmtiCapabilities caps1
;
356 jvmtiJlocationFormat format
;
357 jvmtiEnv
*jvmti
= NULL
;
360 jvmti_agent
= jvmti_open();
362 warnx("jvmti: open_agent failed");
367 * Request a JVMTI interface version 1 environment
369 ret
= (*jvm
)->GetEnv(jvm
, (void *)&jvmti
, JVMTI_VERSION_1
);
371 warnx("jvmti: jvmti version 1 not supported");
376 * acquire method_load capability, we require it
377 * request line numbers (optional)
379 memset(&caps1
, 0, sizeof(caps1
));
380 caps1
.can_generate_compiled_method_load_events
= 1;
382 ret
= (*jvmti
)->AddCapabilities(jvmti
, &caps1
);
383 if (ret
!= JVMTI_ERROR_NONE
) {
384 print_error(jvmti
, "AddCapabilities", ret
);
387 ret
= (*jvmti
)->GetJLocationFormat(jvmti
, &format
);
388 if (ret
== JVMTI_ERROR_NONE
&& format
== JVMTI_JLOCATION_JVMBCI
) {
389 memset(&caps1
, 0, sizeof(caps1
));
390 caps1
.can_get_line_numbers
= 1;
391 caps1
.can_get_source_file_name
= 1;
392 ret
= (*jvmti
)->AddCapabilities(jvmti
, &caps1
);
393 if (ret
== JVMTI_ERROR_NONE
)
394 has_line_numbers
= 1;
395 } else if (ret
!= JVMTI_ERROR_NONE
)
396 print_error(jvmti
, "GetJLocationFormat", ret
);
399 memset(&cb
, 0, sizeof(cb
));
401 cb
.CompiledMethodLoad
= compiled_method_load_cb
;
402 cb
.DynamicCodeGenerated
= code_generated_cb
;
404 ret
= (*jvmti
)->SetEventCallbacks(jvmti
, &cb
, sizeof(cb
));
405 if (ret
!= JVMTI_ERROR_NONE
) {
406 print_error(jvmti
, "SetEventCallbacks", ret
);
410 ret
= (*jvmti
)->SetEventNotificationMode(jvmti
, JVMTI_ENABLE
,
411 JVMTI_EVENT_COMPILED_METHOD_LOAD
, NULL
);
412 if (ret
!= JVMTI_ERROR_NONE
) {
413 print_error(jvmti
, "SetEventNotificationMode(METHOD_LOAD)", ret
);
417 ret
= (*jvmti
)->SetEventNotificationMode(jvmti
, JVMTI_ENABLE
,
418 JVMTI_EVENT_DYNAMIC_CODE_GENERATED
, NULL
);
419 if (ret
!= JVMTI_ERROR_NONE
) {
420 print_error(jvmti
, "SetEventNotificationMode(CODE_GENERATED)", ret
);
426 JNIEXPORT
void JNICALL
427 Agent_OnUnload(JavaVM
*jvm __maybe_unused
)
431 ret
= jvmti_close(jvmti_agent
);
433 errx(1, "Error: op_close_agent()");