1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/compiler.h>
12 #include "jvmti_agent.h"
14 static int has_line_numbers
;
17 static void print_error(jvmtiEnv
*jvmti
, const char *msg
, jvmtiError ret
)
21 err
= (*jvmti
)->GetErrorName(jvmti
, ret
, &err_msg
);
22 if (err
== JVMTI_ERROR_NONE
) {
23 warnx("%s failed with %s", msg
, err_msg
);
24 (*jvmti
)->Deallocate(jvmti
, (unsigned char *)err_msg
);
26 warnx("%s failed with an unknown error %d", msg
, ret
);
31 do_get_line_numbers(jvmtiEnv
*jvmti
, void *pc
, jmethodID m
, jint bci
,
32 jvmti_line_info_t
*tab
, jint
*nr
)
36 jvmtiLineNumberEntry
*loc_tab
= NULL
;
39 ret
= (*jvmti
)->GetLineNumberTable(jvmti
, m
, &nr_lines
, &loc_tab
);
40 if (ret
!= JVMTI_ERROR_NONE
) {
41 print_error(jvmti
, "GetLineNumberTable", ret
);
45 for (i
= 0; i
< nr_lines
; i
++) {
46 if (loc_tab
[i
].start_location
< bci
) {
47 tab
[lines
].pc
= (unsigned long)pc
;
48 tab
[lines
].line_number
= loc_tab
[i
].line_number
;
49 tab
[lines
].discrim
= 0; /* not yet used */
55 (*jvmti
)->Deallocate(jvmti
, (unsigned char *)loc_tab
);
57 return JVMTI_ERROR_NONE
;
61 get_line_numbers(jvmtiEnv
*jvmti
, const void *compile_info
, jvmti_line_info_t
**tab
, int *nr_lines
)
63 const jvmtiCompiledMethodLoadRecordHeader
*hdr
;
64 jvmtiCompiledMethodLoadInlineRecord
*rec
;
65 jvmtiLineNumberEntry
*lne
= NULL
;
69 int i
, lines_total
= 0;
71 if (!(tab
&& nr_lines
))
72 return JVMTI_ERROR_NULL_POINTER
;
75 * Phase 1 -- get the number of lines necessary
77 for (hdr
= compile_info
; hdr
!= NULL
; hdr
= hdr
->next
) {
78 if (hdr
->kind
== JVMTI_CMLR_INLINE_INFO
) {
79 rec
= (jvmtiCompiledMethodLoadInlineRecord
*)hdr
;
80 for (i
= 0; i
< rec
->numpcs
; i
++) {
84 * unfortunately, need a tab to get the number of lines!
86 ret
= (*jvmti
)->GetLineNumberTable(jvmti
, c
->methods
[0], &nr
, &lne
);
87 if (ret
== JVMTI_ERROR_NONE
) {
88 /* free what was allocated for nothing */
89 (*jvmti
)->Deallocate(jvmti
, (unsigned char *)lne
);
92 print_error(jvmti
, "GetLineNumberTable", ret
);
99 return JVMTI_ERROR_NOT_FOUND
;
102 * Phase 2 -- allocate big enough line table
104 *tab
= malloc(nr_total
* sizeof(**tab
));
106 return JVMTI_ERROR_OUT_OF_MEMORY
;
108 for (hdr
= compile_info
; hdr
!= NULL
; hdr
= hdr
->next
) {
109 if (hdr
->kind
== JVMTI_CMLR_INLINE_INFO
) {
110 rec
= (jvmtiCompiledMethodLoadInlineRecord
*)hdr
;
111 for (i
= 0; i
< rec
->numpcs
; i
++) {
114 ret
= do_get_line_numbers(jvmti
, c
->pc
,
119 if (ret
== JVMTI_ERROR_NONE
)
124 *nr_lines
= lines_total
;
125 return JVMTI_ERROR_NONE
;
129 compiled_method_load_cb(jvmtiEnv
*jvmti
,
132 void const *code_addr
,
134 jvmtiAddrLocationMap
const *map
,
135 const void *compile_info
)
137 jvmti_line_info_t
*line_tab
= NULL
;
139 char *class_sign
= NULL
;
140 char *func_name
= NULL
;
141 char *func_sign
= NULL
;
142 char *file_name
= NULL
;
144 uint64_t addr
= (uint64_t)(uintptr_t)code_addr
;
146 int nr_lines
= 0; /* in line_tab[] */
149 ret
= (*jvmti
)->GetMethodDeclaringClass(jvmti
, method
,
151 if (ret
!= JVMTI_ERROR_NONE
) {
152 print_error(jvmti
, "GetMethodDeclaringClass", ret
);
156 if (has_line_numbers
&& map
&& map_length
) {
157 ret
= get_line_numbers(jvmti
, compile_info
, &line_tab
, &nr_lines
);
158 if (ret
!= JVMTI_ERROR_NONE
) {
159 warnx("jvmti: cannot get line table for method");
164 ret
= (*jvmti
)->GetSourceFileName(jvmti
, decl_class
, &file_name
);
165 if (ret
!= JVMTI_ERROR_NONE
) {
166 print_error(jvmti
, "GetSourceFileName", ret
);
170 ret
= (*jvmti
)->GetClassSignature(jvmti
, decl_class
,
172 if (ret
!= JVMTI_ERROR_NONE
) {
173 print_error(jvmti
, "GetClassSignature", ret
);
177 ret
= (*jvmti
)->GetMethodName(jvmti
, method
, &func_name
,
179 if (ret
!= JVMTI_ERROR_NONE
) {
180 print_error(jvmti
, "GetMethodName", ret
);
185 * Assume path name is class hierarchy, this is a common practice with Java programs
187 if (*class_sign
== 'L') {
189 char *p
= strrchr(class_sign
, '/');
191 /* drop the 'L' prefix and copy up to the final '/' */
192 for (i
= 0; i
< (p
- class_sign
); i
++)
193 fn
[i
] = class_sign
[i
+1];
196 * append file name, we use loops and not string ops to avoid modifying
197 * class_sign which is used later for the symbol name
199 for (j
= 0; i
< (PATH_MAX
- 1) && file_name
&& j
< strlen(file_name
); j
++, i
++)
200 fn
[i
] = file_name
[j
];
204 strcpy(fn
, file_name
);
207 * write source line info record if we have it
209 if (jvmti_write_debug_info(jvmti_agent
, addr
, fn
, line_tab
, nr_lines
))
210 warnx("jvmti: write_debug_info() failed");
212 len
= strlen(func_name
) + strlen(class_sign
) + strlen(func_sign
) + 2;
215 snprintf(str
, len
, "%s%s%s", class_sign
, func_name
, func_sign
);
217 if (jvmti_write_code(jvmti_agent
, str
, addr
, code_addr
, code_size
))
218 warnx("jvmti: write_code() failed");
221 (*jvmti
)->Deallocate(jvmti
, (unsigned char *)func_name
);
222 (*jvmti
)->Deallocate(jvmti
, (unsigned char *)func_sign
);
223 (*jvmti
)->Deallocate(jvmti
, (unsigned char *)class_sign
);
224 (*jvmti
)->Deallocate(jvmti
, (unsigned char *)file_name
);
229 code_generated_cb(jvmtiEnv
*jvmti
,
231 void const *code_addr
,
234 uint64_t addr
= (uint64_t)(unsigned long)code_addr
;
237 ret
= jvmti_write_code(jvmti_agent
, name
, addr
, code_addr
, code_size
);
239 warnx("jvmti: write_code() failed for code_generated");
242 JNIEXPORT jint JNICALL
243 Agent_OnLoad(JavaVM
*jvm
, char *options
, void *reserved __maybe_unused
)
245 jvmtiEventCallbacks cb
;
246 jvmtiCapabilities caps1
;
247 jvmtiJlocationFormat format
;
248 jvmtiEnv
*jvmti
= NULL
;
251 jvmti_agent
= jvmti_open();
253 warnx("jvmti: open_agent failed");
258 * Request a JVMTI interface version 1 environment
260 ret
= (*jvm
)->GetEnv(jvm
, (void *)&jvmti
, JVMTI_VERSION_1
);
262 warnx("jvmti: jvmti version 1 not supported");
267 * acquire method_load capability, we require it
268 * request line numbers (optional)
270 memset(&caps1
, 0, sizeof(caps1
));
271 caps1
.can_generate_compiled_method_load_events
= 1;
273 ret
= (*jvmti
)->AddCapabilities(jvmti
, &caps1
);
274 if (ret
!= JVMTI_ERROR_NONE
) {
275 print_error(jvmti
, "AddCapabilities", ret
);
278 ret
= (*jvmti
)->GetJLocationFormat(jvmti
, &format
);
279 if (ret
== JVMTI_ERROR_NONE
&& format
== JVMTI_JLOCATION_JVMBCI
) {
280 memset(&caps1
, 0, sizeof(caps1
));
281 caps1
.can_get_line_numbers
= 1;
282 caps1
.can_get_source_file_name
= 1;
283 ret
= (*jvmti
)->AddCapabilities(jvmti
, &caps1
);
284 if (ret
== JVMTI_ERROR_NONE
)
285 has_line_numbers
= 1;
286 } else if (ret
!= JVMTI_ERROR_NONE
)
287 print_error(jvmti
, "GetJLocationFormat", ret
);
290 memset(&cb
, 0, sizeof(cb
));
292 cb
.CompiledMethodLoad
= compiled_method_load_cb
;
293 cb
.DynamicCodeGenerated
= code_generated_cb
;
295 ret
= (*jvmti
)->SetEventCallbacks(jvmti
, &cb
, sizeof(cb
));
296 if (ret
!= JVMTI_ERROR_NONE
) {
297 print_error(jvmti
, "SetEventCallbacks", ret
);
301 ret
= (*jvmti
)->SetEventNotificationMode(jvmti
, JVMTI_ENABLE
,
302 JVMTI_EVENT_COMPILED_METHOD_LOAD
, NULL
);
303 if (ret
!= JVMTI_ERROR_NONE
) {
304 print_error(jvmti
, "SetEventNotificationMode(METHOD_LOAD)", ret
);
308 ret
= (*jvmti
)->SetEventNotificationMode(jvmti
, JVMTI_ENABLE
,
309 JVMTI_EVENT_DYNAMIC_CODE_GENERATED
, NULL
);
310 if (ret
!= JVMTI_ERROR_NONE
) {
311 print_error(jvmti
, "SetEventNotificationMode(CODE_GENERATED)", ret
);
317 JNIEXPORT
void JNICALL
318 Agent_OnUnload(JavaVM
*jvm __maybe_unused
)
322 ret
= jvmti_close(jvmti_agent
);
324 errx(1, "Error: op_close_agent()");