1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/compiler.h>
3 #include <linux/string.h>
10 #ifdef HAVE_JVMTI_CMLR
11 #include <jvmticmlr.h>
15 #include "jvmti_agent.h"
17 static int has_line_numbers
;
20 static void print_error(jvmtiEnv
*jvmti
, const char *msg
, jvmtiError ret
)
24 err
= (*jvmti
)->GetErrorName(jvmti
, ret
, &err_msg
);
25 if (err
== JVMTI_ERROR_NONE
) {
26 warnx("%s failed with %s", msg
, err_msg
);
27 (*jvmti
)->Deallocate(jvmti
, (unsigned char *)err_msg
);
29 warnx("%s failed with an unknown error %d", msg
, ret
);
33 #ifdef HAVE_JVMTI_CMLR
35 do_get_line_number(jvmtiEnv
*jvmti
, void *pc
, jmethodID m
, jint bci
,
36 jvmti_line_info_t
*tab
)
39 jvmtiLineNumberEntry
*loc_tab
= NULL
;
43 ret
= (*jvmti
)->GetLineNumberTable(jvmti
, m
, &nr_lines
, &loc_tab
);
44 if (ret
== JVMTI_ERROR_ABSENT_INFORMATION
|| ret
== JVMTI_ERROR_NATIVE_METHOD
) {
45 /* No debug information for this method */
47 } else if (ret
!= JVMTI_ERROR_NONE
) {
48 print_error(jvmti
, "GetLineNumberTable", ret
);
52 for (i
= 0; i
< nr_lines
&& loc_tab
[i
].start_location
<= bci
; i
++) {
57 tab
->pc
= (unsigned long)pc
;
58 tab
->line_number
= loc_tab
[src_line
].line_number
;
59 tab
->discrim
= 0; /* not yet used */
62 ret
= JVMTI_ERROR_NONE
;
64 ret
= JVMTI_ERROR_ABSENT_INFORMATION
;
67 (*jvmti
)->Deallocate(jvmti
, (unsigned char *)loc_tab
);
73 get_line_numbers(jvmtiEnv
*jvmti
, const void *compile_info
, jvmti_line_info_t
**tab
, int *nr_lines
)
75 const jvmtiCompiledMethodLoadRecordHeader
*hdr
;
76 jvmtiCompiledMethodLoadInlineRecord
*rec
;
80 int i
, lines_total
= 0;
82 if (!(tab
&& nr_lines
))
83 return JVMTI_ERROR_NULL_POINTER
;
86 * Phase 1 -- get the number of lines necessary
88 for (hdr
= compile_info
; hdr
!= NULL
; hdr
= hdr
->next
) {
89 if (hdr
->kind
== JVMTI_CMLR_INLINE_INFO
) {
90 rec
= (jvmtiCompiledMethodLoadInlineRecord
*)hdr
;
91 nr_total
+= rec
->numpcs
;
96 return JVMTI_ERROR_NOT_FOUND
;
99 * Phase 2 -- allocate big enough line table
101 *tab
= malloc(nr_total
* sizeof(**tab
));
103 return JVMTI_ERROR_OUT_OF_MEMORY
;
105 for (hdr
= compile_info
; hdr
!= NULL
; hdr
= hdr
->next
) {
106 if (hdr
->kind
== JVMTI_CMLR_INLINE_INFO
) {
107 rec
= (jvmtiCompiledMethodLoadInlineRecord
*)hdr
;
108 for (i
= 0; i
< rec
->numpcs
; i
++) {
111 * c->methods is the stack of inlined method calls
112 * at c->pc. [0] is the leaf method. Caller frames
113 * are ignored at the moment.
115 ret
= do_get_line_number(jvmti
, c
->pc
,
119 if (ret
== JVMTI_ERROR_NONE
)
124 *nr_lines
= lines_total
;
125 return JVMTI_ERROR_NONE
;
127 #else /* HAVE_JVMTI_CMLR */
130 get_line_numbers(jvmtiEnv
*jvmti __maybe_unused
, const void *compile_info __maybe_unused
,
131 jvmti_line_info_t
**tab __maybe_unused
, int *nr_lines __maybe_unused
)
133 return JVMTI_ERROR_NONE
;
135 #endif /* HAVE_JVMTI_CMLR */
138 copy_class_filename(const char * class_sign
, const char * file_name
, char * result
, size_t max_length
)
141 * Assume path name is class hierarchy, this is a common practice with Java programs
143 if (*class_sign
== 'L') {
145 char *p
= strrchr(class_sign
, '/');
147 /* drop the 'L' prefix and copy up to the final '/' */
148 for (i
= 0; i
< (p
- class_sign
); i
++)
149 result
[i
] = class_sign
[i
+1];
152 * append file name, we use loops and not string ops to avoid modifying
153 * class_sign which is used later for the symbol name
155 for (j
= 0; i
< (max_length
- 1) && file_name
&& j
< strlen(file_name
); j
++, i
++)
156 result
[i
] = file_name
[j
];
161 strlcpy(result
, file_name
, max_length
);
166 get_source_filename(jvmtiEnv
*jvmti
, jmethodID methodID
, char ** buffer
)
170 char *file_name
= NULL
;
171 char *class_sign
= NULL
;
175 ret
= (*jvmti
)->GetMethodDeclaringClass(jvmti
, methodID
, &decl_class
);
176 if (ret
!= JVMTI_ERROR_NONE
) {
177 print_error(jvmti
, "GetMethodDeclaringClass", ret
);
181 ret
= (*jvmti
)->GetSourceFileName(jvmti
, decl_class
, &file_name
);
182 if (ret
!= JVMTI_ERROR_NONE
) {
183 print_error(jvmti
, "GetSourceFileName", ret
);
187 ret
= (*jvmti
)->GetClassSignature(jvmti
, decl_class
, &class_sign
, NULL
);
188 if (ret
!= JVMTI_ERROR_NONE
) {
189 print_error(jvmti
, "GetClassSignature", ret
);
190 goto free_file_name_error
;
193 copy_class_filename(class_sign
, file_name
, fn
, PATH_MAX
);
195 *buffer
= malloc((len
+ 1) * sizeof(char));
197 print_error(jvmti
, "GetClassSignature", ret
);
198 ret
= JVMTI_ERROR_OUT_OF_MEMORY
;
199 goto free_class_sign_error
;
202 ret
= JVMTI_ERROR_NONE
;
204 free_class_sign_error
:
205 (*jvmti
)->Deallocate(jvmti
, (unsigned char *)class_sign
);
206 free_file_name_error
:
207 (*jvmti
)->Deallocate(jvmti
, (unsigned char *)file_name
);
213 fill_source_filenames(jvmtiEnv
*jvmti
, int nr_lines
,
214 const jvmti_line_info_t
* line_tab
,
220 for (index
= 0; index
< nr_lines
; ++index
) {
221 ret
= get_source_filename(jvmti
, line_tab
[index
].methodID
, &(file_names
[index
]));
222 if (ret
!= JVMTI_ERROR_NONE
)
226 return JVMTI_ERROR_NONE
;
230 compiled_method_load_cb(jvmtiEnv
*jvmti
,
233 void const *code_addr
,
235 jvmtiAddrLocationMap
const *map
,
236 const void *compile_info
)
238 jvmti_line_info_t
*line_tab
= NULL
;
239 char ** line_file_names
= NULL
;
241 char *class_sign
= NULL
;
242 char *func_name
= NULL
;
243 char *func_sign
= NULL
;
244 uint64_t addr
= (uint64_t)(uintptr_t)code_addr
;
246 int nr_lines
= 0; /* in line_tab[] */
248 int output_debug_info
= 0;
250 ret
= (*jvmti
)->GetMethodDeclaringClass(jvmti
, method
,
252 if (ret
!= JVMTI_ERROR_NONE
) {
253 print_error(jvmti
, "GetMethodDeclaringClass", ret
);
257 if (has_line_numbers
&& map
&& map_length
) {
258 ret
= get_line_numbers(jvmti
, compile_info
, &line_tab
, &nr_lines
);
259 if (ret
!= JVMTI_ERROR_NONE
) {
260 if (ret
!= JVMTI_ERROR_NOT_FOUND
) {
261 warnx("jvmti: cannot get line table for method");
264 } else if (nr_lines
> 0) {
265 line_file_names
= malloc(sizeof(char*) * nr_lines
);
266 if (!line_file_names
) {
267 warnx("jvmti: cannot allocate space for line table method names");
269 memset(line_file_names
, 0, sizeof(char*) * nr_lines
);
270 ret
= fill_source_filenames(jvmti
, nr_lines
, line_tab
, line_file_names
);
271 if (ret
!= JVMTI_ERROR_NONE
) {
272 warnx("jvmti: fill_source_filenames failed");
274 output_debug_info
= 1;
280 ret
= (*jvmti
)->GetClassSignature(jvmti
, decl_class
,
282 if (ret
!= JVMTI_ERROR_NONE
) {
283 print_error(jvmti
, "GetClassSignature", ret
);
287 ret
= (*jvmti
)->GetMethodName(jvmti
, method
, &func_name
,
289 if (ret
!= JVMTI_ERROR_NONE
) {
290 print_error(jvmti
, "GetMethodName", ret
);
295 * write source line info record if we have it
297 if (output_debug_info
)
298 if (jvmti_write_debug_info(jvmti_agent
, addr
, nr_lines
, line_tab
, (const char * const *) line_file_names
))
299 warnx("jvmti: write_debug_info() failed");
301 len
= strlen(func_name
) + strlen(class_sign
) + strlen(func_sign
) + 2;
304 snprintf(str
, len
, "%s%s%s", class_sign
, func_name
, func_sign
);
306 if (jvmti_write_code(jvmti_agent
, str
, addr
, code_addr
, code_size
))
307 warnx("jvmti: write_code() failed");
310 (*jvmti
)->Deallocate(jvmti
, (unsigned char *)func_name
);
311 (*jvmti
)->Deallocate(jvmti
, (unsigned char *)func_sign
);
312 (*jvmti
)->Deallocate(jvmti
, (unsigned char *)class_sign
);
314 while (line_file_names
&& (nr_lines
> 0)) {
315 if (line_file_names
[nr_lines
- 1]) {
316 free(line_file_names
[nr_lines
- 1]);
320 free(line_file_names
);
324 code_generated_cb(jvmtiEnv
*jvmti
,
326 void const *code_addr
,
329 uint64_t addr
= (uint64_t)(unsigned long)code_addr
;
332 ret
= jvmti_write_code(jvmti_agent
, name
, addr
, code_addr
, code_size
);
334 warnx("jvmti: write_code() failed for code_generated");
337 JNIEXPORT jint JNICALL
338 Agent_OnLoad(JavaVM
*jvm
, char *options
, void *reserved __maybe_unused
)
340 jvmtiEventCallbacks cb
;
341 jvmtiCapabilities caps1
;
342 jvmtiJlocationFormat format
;
343 jvmtiEnv
*jvmti
= NULL
;
346 jvmti_agent
= jvmti_open();
348 warnx("jvmti: open_agent failed");
353 * Request a JVMTI interface version 1 environment
355 ret
= (*jvm
)->GetEnv(jvm
, (void *)&jvmti
, JVMTI_VERSION_1
);
357 warnx("jvmti: jvmti version 1 not supported");
362 * acquire method_load capability, we require it
363 * request line numbers (optional)
365 memset(&caps1
, 0, sizeof(caps1
));
366 caps1
.can_generate_compiled_method_load_events
= 1;
368 ret
= (*jvmti
)->AddCapabilities(jvmti
, &caps1
);
369 if (ret
!= JVMTI_ERROR_NONE
) {
370 print_error(jvmti
, "AddCapabilities", ret
);
373 ret
= (*jvmti
)->GetJLocationFormat(jvmti
, &format
);
374 if (ret
== JVMTI_ERROR_NONE
&& format
== JVMTI_JLOCATION_JVMBCI
) {
375 memset(&caps1
, 0, sizeof(caps1
));
376 caps1
.can_get_line_numbers
= 1;
377 caps1
.can_get_source_file_name
= 1;
378 ret
= (*jvmti
)->AddCapabilities(jvmti
, &caps1
);
379 if (ret
== JVMTI_ERROR_NONE
)
380 has_line_numbers
= 1;
381 } else if (ret
!= JVMTI_ERROR_NONE
)
382 print_error(jvmti
, "GetJLocationFormat", ret
);
385 memset(&cb
, 0, sizeof(cb
));
387 cb
.CompiledMethodLoad
= compiled_method_load_cb
;
388 cb
.DynamicCodeGenerated
= code_generated_cb
;
390 ret
= (*jvmti
)->SetEventCallbacks(jvmti
, &cb
, sizeof(cb
));
391 if (ret
!= JVMTI_ERROR_NONE
) {
392 print_error(jvmti
, "SetEventCallbacks", ret
);
396 ret
= (*jvmti
)->SetEventNotificationMode(jvmti
, JVMTI_ENABLE
,
397 JVMTI_EVENT_COMPILED_METHOD_LOAD
, NULL
);
398 if (ret
!= JVMTI_ERROR_NONE
) {
399 print_error(jvmti
, "SetEventNotificationMode(METHOD_LOAD)", ret
);
403 ret
= (*jvmti
)->SetEventNotificationMode(jvmti
, JVMTI_ENABLE
,
404 JVMTI_EVENT_DYNAMIC_CODE_GENERATED
, NULL
);
405 if (ret
!= JVMTI_ERROR_NONE
) {
406 print_error(jvmti
, "SetEventNotificationMode(CODE_GENERATED)", ret
);
412 JNIEXPORT
void JNICALL
413 Agent_OnUnload(JavaVM
*jvm __maybe_unused
)
417 ret
= jvmti_close(jvmti_agent
);
419 errx(1, "Error: op_close_agent()");