Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/perf/jvmti/libjvmti.c
26285 views
1
// SPDX-License-Identifier: GPL-2.0
2
#include <linux/compiler.h>
3
#include <linux/string.h>
4
#include <sys/types.h>
5
#include <stdio.h>
6
#include <string.h>
7
#include <stdlib.h>
8
#include <err.h>
9
#include <jvmti.h>
10
#ifdef HAVE_JVMTI_CMLR
11
#include <jvmticmlr.h>
12
#endif
13
#include <limits.h>
14
15
#include "jvmti_agent.h"
16
17
static int has_line_numbers;
18
void *jvmti_agent;
19
20
static void print_error(jvmtiEnv *jvmti, const char *msg, jvmtiError ret)
21
{
22
char *err_msg = NULL;
23
jvmtiError err;
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);
28
} else {
29
warnx("%s failed with an unknown error %d", msg, ret);
30
}
31
}
32
33
#ifdef HAVE_JVMTI_CMLR
34
static jvmtiError
35
do_get_line_number(jvmtiEnv *jvmti, void *pc, jmethodID m, jint bci,
36
jvmti_line_info_t *tab)
37
{
38
jint i, nr_lines = 0;
39
jvmtiLineNumberEntry *loc_tab = NULL;
40
jvmtiError ret;
41
jint src_line = -1;
42
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 */
46
return ret;
47
} else if (ret != JVMTI_ERROR_NONE) {
48
print_error(jvmti, "GetLineNumberTable", ret);
49
return ret;
50
}
51
52
for (i = 0; i < nr_lines && loc_tab[i].start_location <= bci; i++) {
53
src_line = i;
54
}
55
56
if (src_line != -1) {
57
tab->pc = (unsigned long)pc;
58
tab->line_number = loc_tab[src_line].line_number;
59
tab->discrim = 0; /* not yet used */
60
tab->methodID = m;
61
62
ret = JVMTI_ERROR_NONE;
63
} else {
64
ret = JVMTI_ERROR_ABSENT_INFORMATION;
65
}
66
67
(*jvmti)->Deallocate(jvmti, (unsigned char *)loc_tab);
68
69
return ret;
70
}
71
72
static jvmtiError
73
get_line_numbers(jvmtiEnv *jvmti, const void *compile_info, jvmti_line_info_t **tab, int *nr_lines)
74
{
75
const jvmtiCompiledMethodLoadRecordHeader *hdr;
76
jvmtiCompiledMethodLoadInlineRecord *rec;
77
PCStackInfo *c;
78
jint ret;
79
int nr_total = 0;
80
int i, lines_total = 0;
81
82
if (!(tab && nr_lines))
83
return JVMTI_ERROR_NULL_POINTER;
84
85
/*
86
* Phase 1 -- get the number of lines necessary
87
*/
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;
92
}
93
}
94
95
if (nr_total == 0)
96
return JVMTI_ERROR_NOT_FOUND;
97
98
/*
99
* Phase 2 -- allocate big enough line table
100
*/
101
*tab = malloc(nr_total * sizeof(**tab));
102
if (!*tab)
103
return JVMTI_ERROR_OUT_OF_MEMORY;
104
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++) {
109
c = rec->pcinfo + i;
110
/*
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.
114
*/
115
ret = do_get_line_number(jvmti, c->pc,
116
c->methods[0],
117
c->bcis[0],
118
*tab + lines_total);
119
if (ret == JVMTI_ERROR_NONE)
120
lines_total++;
121
}
122
}
123
}
124
*nr_lines = lines_total;
125
return JVMTI_ERROR_NONE;
126
}
127
#else /* HAVE_JVMTI_CMLR */
128
129
static jvmtiError
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)
132
{
133
return JVMTI_ERROR_NONE;
134
}
135
#endif /* HAVE_JVMTI_CMLR */
136
137
static void
138
copy_class_filename(const char * class_sign, const char * file_name, char * result, size_t max_length)
139
{
140
/*
141
* Assume path name is class hierarchy, this is a common practice with Java programs
142
*/
143
if (*class_sign == 'L') {
144
size_t j, i = 0;
145
char *p = strrchr(class_sign, '/');
146
if (p) {
147
/* drop the 'L' prefix and copy up to the final '/' */
148
for (i = 0; i < (size_t)(p - class_sign); i++)
149
result[i] = class_sign[i+1];
150
}
151
/*
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
154
*/
155
for (j = 0; i < (max_length - 1) && file_name && j < strlen(file_name); j++, i++)
156
result[i] = file_name[j];
157
158
result[i] = '\0';
159
} else {
160
/* fallback case */
161
strlcpy(result, file_name, max_length);
162
}
163
}
164
165
static jvmtiError
166
get_source_filename(jvmtiEnv *jvmti, jmethodID methodID, char ** buffer)
167
{
168
jvmtiError ret;
169
jclass decl_class;
170
char *file_name = NULL;
171
char *class_sign = NULL;
172
char fn[PATH_MAX];
173
size_t len;
174
175
ret = (*jvmti)->GetMethodDeclaringClass(jvmti, methodID, &decl_class);
176
if (ret != JVMTI_ERROR_NONE) {
177
print_error(jvmti, "GetMethodDeclaringClass", ret);
178
return ret;
179
}
180
181
ret = (*jvmti)->GetSourceFileName(jvmti, decl_class, &file_name);
182
if (ret != JVMTI_ERROR_NONE) {
183
print_error(jvmti, "GetSourceFileName", ret);
184
return ret;
185
}
186
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;
191
}
192
193
copy_class_filename(class_sign, file_name, fn, PATH_MAX);
194
len = strlen(fn);
195
*buffer = malloc((len + 1) * sizeof(char));
196
if (!*buffer) {
197
print_error(jvmti, "GetClassSignature", ret);
198
ret = JVMTI_ERROR_OUT_OF_MEMORY;
199
goto free_class_sign_error;
200
}
201
strcpy(*buffer, fn);
202
ret = JVMTI_ERROR_NONE;
203
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);
208
209
return ret;
210
}
211
212
static jvmtiError
213
fill_source_filenames(jvmtiEnv *jvmti, int nr_lines,
214
const jvmti_line_info_t * line_tab,
215
char ** file_names)
216
{
217
int index;
218
jvmtiError ret;
219
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)
223
return ret;
224
}
225
226
return JVMTI_ERROR_NONE;
227
}
228
229
static void JNICALL
230
compiled_method_load_cb(jvmtiEnv *jvmti,
231
jmethodID method,
232
jint code_size,
233
void const *code_addr,
234
jint map_length,
235
jvmtiAddrLocationMap const *map,
236
const void *compile_info)
237
{
238
jvmti_line_info_t *line_tab = NULL;
239
char ** line_file_names = NULL;
240
jclass decl_class;
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;
245
jvmtiError ret;
246
int nr_lines = 0; /* in line_tab[] */
247
size_t len;
248
int output_debug_info = 0;
249
250
ret = (*jvmti)->GetMethodDeclaringClass(jvmti, method,
251
&decl_class);
252
if (ret != JVMTI_ERROR_NONE) {
253
print_error(jvmti, "GetMethodDeclaringClass", ret);
254
return;
255
}
256
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");
262
}
263
nr_lines = 0;
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");
268
} else {
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");
273
} else {
274
output_debug_info = 1;
275
}
276
}
277
}
278
}
279
280
ret = (*jvmti)->GetClassSignature(jvmti, decl_class,
281
&class_sign, NULL);
282
if (ret != JVMTI_ERROR_NONE) {
283
print_error(jvmti, "GetClassSignature", ret);
284
goto error;
285
}
286
287
ret = (*jvmti)->GetMethodName(jvmti, method, &func_name,
288
&func_sign, NULL);
289
if (ret != JVMTI_ERROR_NONE) {
290
print_error(jvmti, "GetMethodName", ret);
291
goto error;
292
}
293
294
/*
295
* write source line info record if we have it
296
*/
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");
300
301
len = strlen(func_name) + strlen(class_sign) + strlen(func_sign) + 2;
302
{
303
char str[len];
304
snprintf(str, len, "%s%s%s", class_sign, func_name, func_sign);
305
306
if (jvmti_write_code(jvmti_agent, str, addr, code_addr, code_size))
307
warnx("jvmti: write_code() failed");
308
}
309
error:
310
(*jvmti)->Deallocate(jvmti, (unsigned char *)func_name);
311
(*jvmti)->Deallocate(jvmti, (unsigned char *)func_sign);
312
(*jvmti)->Deallocate(jvmti, (unsigned char *)class_sign);
313
free(line_tab);
314
while (line_file_names && (nr_lines > 0)) {
315
if (line_file_names[nr_lines - 1]) {
316
free(line_file_names[nr_lines - 1]);
317
}
318
nr_lines -= 1;
319
}
320
free(line_file_names);
321
}
322
323
static void JNICALL
324
code_generated_cb(jvmtiEnv *jvmti,
325
char const *name,
326
void const *code_addr,
327
jint code_size)
328
{
329
uint64_t addr = (uint64_t)(unsigned long)code_addr;
330
int ret;
331
332
ret = jvmti_write_code(jvmti_agent, name, addr, code_addr, code_size);
333
if (ret)
334
warnx("jvmti: write_code() failed for code_generated");
335
}
336
337
JNIEXPORT jint JNICALL
338
Agent_OnLoad(JavaVM *jvm, char *options, void *reserved __maybe_unused)
339
{
340
jvmtiEventCallbacks cb;
341
jvmtiCapabilities caps1;
342
jvmtiJlocationFormat format;
343
jvmtiEnv *jvmti = NULL;
344
jint ret;
345
346
jvmti_agent = jvmti_open();
347
if (!jvmti_agent) {
348
warnx("jvmti: open_agent failed");
349
return -1;
350
}
351
352
/*
353
* Request a JVMTI interface version 1 environment
354
*/
355
ret = (*jvm)->GetEnv(jvm, (void *)&jvmti, JVMTI_VERSION_1);
356
if (ret != JNI_OK) {
357
warnx("jvmti: jvmti version 1 not supported");
358
return -1;
359
}
360
361
/*
362
* acquire method_load capability, we require it
363
* request line numbers (optional)
364
*/
365
memset(&caps1, 0, sizeof(caps1));
366
caps1.can_generate_compiled_method_load_events = 1;
367
368
ret = (*jvmti)->AddCapabilities(jvmti, &caps1);
369
if (ret != JVMTI_ERROR_NONE) {
370
print_error(jvmti, "AddCapabilities", ret);
371
return -1;
372
}
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);
383
384
385
memset(&cb, 0, sizeof(cb));
386
387
cb.CompiledMethodLoad = compiled_method_load_cb;
388
cb.DynamicCodeGenerated = code_generated_cb;
389
390
ret = (*jvmti)->SetEventCallbacks(jvmti, &cb, sizeof(cb));
391
if (ret != JVMTI_ERROR_NONE) {
392
print_error(jvmti, "SetEventCallbacks", ret);
393
return -1;
394
}
395
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);
400
return -1;
401
}
402
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);
407
return -1;
408
}
409
return 0;
410
}
411
412
JNIEXPORT void JNICALL
413
Agent_OnUnload(JavaVM *jvm __maybe_unused)
414
{
415
int ret;
416
417
ret = jvmti_close(jvmti_agent);
418
if (ret)
419
errx(1, "Error: op_close_agent()");
420
}
421
422