1 #include <linux/compiler.h>
11 #include "jvmti_agent.h"
13 static int has_line_numbers
;
16 static void print_error(jvmtiEnv
*jvmti
, const char *msg
, jvmtiError ret
)
20 err
= (*jvmti
)->GetErrorName(jvmti
, ret
, &err_msg
);
21 if (err
== JVMTI_ERROR_NONE
) {
22 warnx("%s failed with %s", msg
, err_msg
);
23 (*jvmti
)->Deallocate(jvmti
, (unsigned char *)err_msg
);
25 warnx("%s failed with an unknown error %d", msg
, ret
);
30 do_get_line_numbers(jvmtiEnv
*jvmti
, void *pc
, jmethodID m
, jint bci
,
31 jvmti_line_info_t
*tab
, jint
*nr
)
35 jvmtiLineNumberEntry
*loc_tab
= NULL
;
38 ret
= (*jvmti
)->GetLineNumberTable(jvmti
, m
, &nr_lines
, &loc_tab
);
39 if (ret
!= JVMTI_ERROR_NONE
) {
40 print_error(jvmti
, "GetLineNumberTable", ret
);
44 for (i
= 0; i
< nr_lines
; i
++) {
45 if (loc_tab
[i
].start_location
< bci
) {
46 tab
[lines
].pc
= (unsigned long)pc
;
47 tab
[lines
].line_number
= loc_tab
[i
].line_number
;
48 tab
[lines
].discrim
= 0; /* not yet used */
54 (*jvmti
)->Deallocate(jvmti
, (unsigned char *)loc_tab
);
56 return JVMTI_ERROR_NONE
;
60 get_line_numbers(jvmtiEnv
*jvmti
, const void *compile_info
, jvmti_line_info_t
**tab
, int *nr_lines
)
62 const jvmtiCompiledMethodLoadRecordHeader
*hdr
;
63 jvmtiCompiledMethodLoadInlineRecord
*rec
;
64 jvmtiLineNumberEntry
*lne
= NULL
;
68 int i
, lines_total
= 0;
70 if (!(tab
&& nr_lines
))
71 return JVMTI_ERROR_NULL_POINTER
;
74 * Phase 1 -- get the number of lines necessary
76 for (hdr
= compile_info
; hdr
!= NULL
; hdr
= hdr
->next
) {
77 if (hdr
->kind
== JVMTI_CMLR_INLINE_INFO
) {
78 rec
= (jvmtiCompiledMethodLoadInlineRecord
*)hdr
;
79 for (i
= 0; i
< rec
->numpcs
; i
++) {
83 * unfortunately, need a tab to get the number of lines!
85 ret
= (*jvmti
)->GetLineNumberTable(jvmti
, c
->methods
[0], &nr
, &lne
);
86 if (ret
== JVMTI_ERROR_NONE
) {
87 /* free what was allocated for nothing */
88 (*jvmti
)->Deallocate(jvmti
, (unsigned char *)lne
);
91 print_error(jvmti
, "GetLineNumberTable", ret
);
98 return JVMTI_ERROR_NOT_FOUND
;
101 * Phase 2 -- allocate big enough line table
103 *tab
= malloc(nr_total
* sizeof(**tab
));
105 return JVMTI_ERROR_OUT_OF_MEMORY
;
107 for (hdr
= compile_info
; hdr
!= NULL
; hdr
= hdr
->next
) {
108 if (hdr
->kind
== JVMTI_CMLR_INLINE_INFO
) {
109 rec
= (jvmtiCompiledMethodLoadInlineRecord
*)hdr
;
110 for (i
= 0; i
< rec
->numpcs
; i
++) {
113 ret
= do_get_line_numbers(jvmti
, c
->pc
,
118 if (ret
== JVMTI_ERROR_NONE
)
123 *nr_lines
= lines_total
;
124 return JVMTI_ERROR_NONE
;
128 compiled_method_load_cb(jvmtiEnv
*jvmti
,
131 void const *code_addr
,
133 jvmtiAddrLocationMap
const *map
,
134 const void *compile_info
)
136 jvmti_line_info_t
*line_tab
= NULL
;
138 char *class_sign
= NULL
;
139 char *func_name
= NULL
;
140 char *func_sign
= NULL
;
141 char *file_name
= NULL
;
143 uint64_t addr
= (uint64_t)(uintptr_t)code_addr
;
145 int nr_lines
= 0; /* in line_tab[] */
148 ret
= (*jvmti
)->GetMethodDeclaringClass(jvmti
, method
,
150 if (ret
!= JVMTI_ERROR_NONE
) {
151 print_error(jvmti
, "GetMethodDeclaringClass", ret
);
155 if (has_line_numbers
&& map
&& map_length
) {
156 ret
= get_line_numbers(jvmti
, compile_info
, &line_tab
, &nr_lines
);
157 if (ret
!= JVMTI_ERROR_NONE
) {
158 warnx("jvmti: cannot get line table for method");
163 ret
= (*jvmti
)->GetSourceFileName(jvmti
, decl_class
, &file_name
);
164 if (ret
!= JVMTI_ERROR_NONE
) {
165 print_error(jvmti
, "GetSourceFileName", ret
);
169 ret
= (*jvmti
)->GetClassSignature(jvmti
, decl_class
,
171 if (ret
!= JVMTI_ERROR_NONE
) {
172 print_error(jvmti
, "GetClassSignature", ret
);
176 ret
= (*jvmti
)->GetMethodName(jvmti
, method
, &func_name
,
178 if (ret
!= JVMTI_ERROR_NONE
) {
179 print_error(jvmti
, "GetMethodName", ret
);
184 * Assume path name is class hierarchy, this is a common practice with Java programs
186 if (*class_sign
== 'L') {
188 char *p
= strrchr(class_sign
, '/');
190 /* drop the 'L' prefix and copy up to the final '/' */
191 for (i
= 0; i
< (p
- class_sign
); i
++)
192 fn
[i
] = class_sign
[i
+1];
195 * append file name, we use loops and not string ops to avoid modifying
196 * class_sign which is used later for the symbol name
198 for (j
= 0; i
< (PATH_MAX
- 1) && file_name
&& j
< strlen(file_name
); j
++, i
++)
199 fn
[i
] = file_name
[j
];
203 strcpy(fn
, file_name
);
206 * write source line info record if we have it
208 if (jvmti_write_debug_info(jvmti_agent
, addr
, fn
, line_tab
, nr_lines
))
209 warnx("jvmti: write_debug_info() failed");
211 len
= strlen(func_name
) + strlen(class_sign
) + strlen(func_sign
) + 2;
214 snprintf(str
, len
, "%s%s%s", class_sign
, func_name
, func_sign
);
216 if (jvmti_write_code(jvmti_agent
, str
, addr
, code_addr
, code_size
))
217 warnx("jvmti: write_code() failed");
220 (*jvmti
)->Deallocate(jvmti
, (unsigned char *)func_name
);
221 (*jvmti
)->Deallocate(jvmti
, (unsigned char *)func_sign
);
222 (*jvmti
)->Deallocate(jvmti
, (unsigned char *)class_sign
);
223 (*jvmti
)->Deallocate(jvmti
, (unsigned char *)file_name
);
228 code_generated_cb(jvmtiEnv
*jvmti
,
230 void const *code_addr
,
233 uint64_t addr
= (uint64_t)(unsigned long)code_addr
;
236 ret
= jvmti_write_code(jvmti_agent
, name
, addr
, code_addr
, code_size
);
238 warnx("jvmti: write_code() failed for code_generated");
241 JNIEXPORT jint JNICALL
242 Agent_OnLoad(JavaVM
*jvm
, char *options
, void *reserved __maybe_unused
)
244 jvmtiEventCallbacks cb
;
245 jvmtiCapabilities caps1
;
246 jvmtiJlocationFormat format
;
247 jvmtiEnv
*jvmti
= NULL
;
250 jvmti_agent
= jvmti_open();
252 warnx("jvmti: open_agent failed");
257 * Request a JVMTI interface version 1 environment
259 ret
= (*jvm
)->GetEnv(jvm
, (void *)&jvmti
, JVMTI_VERSION_1
);
261 warnx("jvmti: jvmti version 1 not supported");
266 * acquire method_load capability, we require it
267 * request line numbers (optional)
269 memset(&caps1
, 0, sizeof(caps1
));
270 caps1
.can_generate_compiled_method_load_events
= 1;
272 ret
= (*jvmti
)->AddCapabilities(jvmti
, &caps1
);
273 if (ret
!= JVMTI_ERROR_NONE
) {
274 print_error(jvmti
, "AddCapabilities", ret
);
277 ret
= (*jvmti
)->GetJLocationFormat(jvmti
, &format
);
278 if (ret
== JVMTI_ERROR_NONE
&& format
== JVMTI_JLOCATION_JVMBCI
) {
279 memset(&caps1
, 0, sizeof(caps1
));
280 caps1
.can_get_line_numbers
= 1;
281 caps1
.can_get_source_file_name
= 1;
282 ret
= (*jvmti
)->AddCapabilities(jvmti
, &caps1
);
283 if (ret
== JVMTI_ERROR_NONE
)
284 has_line_numbers
= 1;
285 } else if (ret
!= JVMTI_ERROR_NONE
)
286 print_error(jvmti
, "GetJLocationFormat", ret
);
289 memset(&cb
, 0, sizeof(cb
));
291 cb
.CompiledMethodLoad
= compiled_method_load_cb
;
292 cb
.DynamicCodeGenerated
= code_generated_cb
;
294 ret
= (*jvmti
)->SetEventCallbacks(jvmti
, &cb
, sizeof(cb
));
295 if (ret
!= JVMTI_ERROR_NONE
) {
296 print_error(jvmti
, "SetEventCallbacks", ret
);
300 ret
= (*jvmti
)->SetEventNotificationMode(jvmti
, JVMTI_ENABLE
,
301 JVMTI_EVENT_COMPILED_METHOD_LOAD
, NULL
);
302 if (ret
!= JVMTI_ERROR_NONE
) {
303 print_error(jvmti
, "SetEventNotificationMode(METHOD_LOAD)", ret
);
307 ret
= (*jvmti
)->SetEventNotificationMode(jvmti
, JVMTI_ENABLE
,
308 JVMTI_EVENT_DYNAMIC_CODE_GENERATED
, NULL
);
309 if (ret
!= JVMTI_ERROR_NONE
) {
310 print_error(jvmti
, "SetEventNotificationMode(CODE_GENERATED)", ret
);
316 JNIEXPORT
void JNICALL
317 Agent_OnUnload(JavaVM
*jvm __maybe_unused
)
321 ret
= jvmti_close(jvmti_agent
);
323 errx(1, "Error: op_close_agent()");