0001
0002 #include <linux/compiler.h>
0003 #include <linux/string.h>
0004 #include <sys/types.h>
0005 #include <stdio.h>
0006 #include <string.h>
0007 #include <stdlib.h>
0008 #include <err.h>
0009 #include <jvmti.h>
0010 #ifdef HAVE_JVMTI_CMLR
0011 #include <jvmticmlr.h>
0012 #endif
0013 #include <limits.h>
0014
0015 #include "jvmti_agent.h"
0016
0017 static int has_line_numbers;
0018 void *jvmti_agent;
0019
0020 static void print_error(jvmtiEnv *jvmti, const char *msg, jvmtiError ret)
0021 {
0022 char *err_msg = NULL;
0023 jvmtiError err;
0024 err = (*jvmti)->GetErrorName(jvmti, ret, &err_msg);
0025 if (err == JVMTI_ERROR_NONE) {
0026 warnx("%s failed with %s", msg, err_msg);
0027 (*jvmti)->Deallocate(jvmti, (unsigned char *)err_msg);
0028 } else {
0029 warnx("%s failed with an unknown error %d", msg, ret);
0030 }
0031 }
0032
0033 #ifdef HAVE_JVMTI_CMLR
0034 static jvmtiError
0035 do_get_line_number(jvmtiEnv *jvmti, void *pc, jmethodID m, jint bci,
0036 jvmti_line_info_t *tab)
0037 {
0038 jint i, nr_lines = 0;
0039 jvmtiLineNumberEntry *loc_tab = NULL;
0040 jvmtiError ret;
0041 jint src_line = -1;
0042
0043 ret = (*jvmti)->GetLineNumberTable(jvmti, m, &nr_lines, &loc_tab);
0044 if (ret == JVMTI_ERROR_ABSENT_INFORMATION || ret == JVMTI_ERROR_NATIVE_METHOD) {
0045
0046 return ret;
0047 } else if (ret != JVMTI_ERROR_NONE) {
0048 print_error(jvmti, "GetLineNumberTable", ret);
0049 return ret;
0050 }
0051
0052 for (i = 0; i < nr_lines && loc_tab[i].start_location <= bci; i++) {
0053 src_line = i;
0054 }
0055
0056 if (src_line != -1) {
0057 tab->pc = (unsigned long)pc;
0058 tab->line_number = loc_tab[src_line].line_number;
0059 tab->discrim = 0;
0060 tab->methodID = m;
0061
0062 ret = JVMTI_ERROR_NONE;
0063 } else {
0064 ret = JVMTI_ERROR_ABSENT_INFORMATION;
0065 }
0066
0067 (*jvmti)->Deallocate(jvmti, (unsigned char *)loc_tab);
0068
0069 return ret;
0070 }
0071
0072 static jvmtiError
0073 get_line_numbers(jvmtiEnv *jvmti, const void *compile_info, jvmti_line_info_t **tab, int *nr_lines)
0074 {
0075 const jvmtiCompiledMethodLoadRecordHeader *hdr;
0076 jvmtiCompiledMethodLoadInlineRecord *rec;
0077 PCStackInfo *c;
0078 jint ret;
0079 int nr_total = 0;
0080 int i, lines_total = 0;
0081
0082 if (!(tab && nr_lines))
0083 return JVMTI_ERROR_NULL_POINTER;
0084
0085
0086
0087
0088 for (hdr = compile_info; hdr != NULL; hdr = hdr->next) {
0089 if (hdr->kind == JVMTI_CMLR_INLINE_INFO) {
0090 rec = (jvmtiCompiledMethodLoadInlineRecord *)hdr;
0091 nr_total += rec->numpcs;
0092 }
0093 }
0094
0095 if (nr_total == 0)
0096 return JVMTI_ERROR_NOT_FOUND;
0097
0098
0099
0100
0101 *tab = malloc(nr_total * sizeof(**tab));
0102 if (!*tab)
0103 return JVMTI_ERROR_OUT_OF_MEMORY;
0104
0105 for (hdr = compile_info; hdr != NULL; hdr = hdr->next) {
0106 if (hdr->kind == JVMTI_CMLR_INLINE_INFO) {
0107 rec = (jvmtiCompiledMethodLoadInlineRecord *)hdr;
0108 for (i = 0; i < rec->numpcs; i++) {
0109 c = rec->pcinfo + i;
0110
0111
0112
0113
0114
0115 ret = do_get_line_number(jvmti, c->pc,
0116 c->methods[0],
0117 c->bcis[0],
0118 *tab + lines_total);
0119 if (ret == JVMTI_ERROR_NONE)
0120 lines_total++;
0121 }
0122 }
0123 }
0124 *nr_lines = lines_total;
0125 return JVMTI_ERROR_NONE;
0126 }
0127 #else
0128
0129 static jvmtiError
0130 get_line_numbers(jvmtiEnv *jvmti __maybe_unused, const void *compile_info __maybe_unused,
0131 jvmti_line_info_t **tab __maybe_unused, int *nr_lines __maybe_unused)
0132 {
0133 return JVMTI_ERROR_NONE;
0134 }
0135 #endif
0136
0137 static void
0138 copy_class_filename(const char * class_sign, const char * file_name, char * result, size_t max_length)
0139 {
0140
0141
0142
0143 if (*class_sign == 'L') {
0144 int j, i = 0;
0145 char *p = strrchr(class_sign, '/');
0146 if (p) {
0147
0148 for (i = 0; i < (p - class_sign); i++)
0149 result[i] = class_sign[i+1];
0150 }
0151
0152
0153
0154
0155 for (j = 0; i < (max_length - 1) && file_name && j < strlen(file_name); j++, i++)
0156 result[i] = file_name[j];
0157
0158 result[i] = '\0';
0159 } else {
0160
0161 strlcpy(result, file_name, max_length);
0162 }
0163 }
0164
0165 static jvmtiError
0166 get_source_filename(jvmtiEnv *jvmti, jmethodID methodID, char ** buffer)
0167 {
0168 jvmtiError ret;
0169 jclass decl_class;
0170 char *file_name = NULL;
0171 char *class_sign = NULL;
0172 char fn[PATH_MAX];
0173 size_t len;
0174
0175 ret = (*jvmti)->GetMethodDeclaringClass(jvmti, methodID, &decl_class);
0176 if (ret != JVMTI_ERROR_NONE) {
0177 print_error(jvmti, "GetMethodDeclaringClass", ret);
0178 return ret;
0179 }
0180
0181 ret = (*jvmti)->GetSourceFileName(jvmti, decl_class, &file_name);
0182 if (ret != JVMTI_ERROR_NONE) {
0183 print_error(jvmti, "GetSourceFileName", ret);
0184 return ret;
0185 }
0186
0187 ret = (*jvmti)->GetClassSignature(jvmti, decl_class, &class_sign, NULL);
0188 if (ret != JVMTI_ERROR_NONE) {
0189 print_error(jvmti, "GetClassSignature", ret);
0190 goto free_file_name_error;
0191 }
0192
0193 copy_class_filename(class_sign, file_name, fn, PATH_MAX);
0194 len = strlen(fn);
0195 *buffer = malloc((len + 1) * sizeof(char));
0196 if (!*buffer) {
0197 print_error(jvmti, "GetClassSignature", ret);
0198 ret = JVMTI_ERROR_OUT_OF_MEMORY;
0199 goto free_class_sign_error;
0200 }
0201 strcpy(*buffer, fn);
0202 ret = JVMTI_ERROR_NONE;
0203
0204 free_class_sign_error:
0205 (*jvmti)->Deallocate(jvmti, (unsigned char *)class_sign);
0206 free_file_name_error:
0207 (*jvmti)->Deallocate(jvmti, (unsigned char *)file_name);
0208
0209 return ret;
0210 }
0211
0212 static jvmtiError
0213 fill_source_filenames(jvmtiEnv *jvmti, int nr_lines,
0214 const jvmti_line_info_t * line_tab,
0215 char ** file_names)
0216 {
0217 int index;
0218 jvmtiError ret;
0219
0220 for (index = 0; index < nr_lines; ++index) {
0221 ret = get_source_filename(jvmti, line_tab[index].methodID, &(file_names[index]));
0222 if (ret != JVMTI_ERROR_NONE)
0223 return ret;
0224 }
0225
0226 return JVMTI_ERROR_NONE;
0227 }
0228
0229 static void JNICALL
0230 compiled_method_load_cb(jvmtiEnv *jvmti,
0231 jmethodID method,
0232 jint code_size,
0233 void const *code_addr,
0234 jint map_length,
0235 jvmtiAddrLocationMap const *map,
0236 const void *compile_info)
0237 {
0238 jvmti_line_info_t *line_tab = NULL;
0239 char ** line_file_names = NULL;
0240 jclass decl_class;
0241 char *class_sign = NULL;
0242 char *func_name = NULL;
0243 char *func_sign = NULL;
0244 uint64_t addr = (uint64_t)(uintptr_t)code_addr;
0245 jvmtiError ret;
0246 int nr_lines = 0;
0247 size_t len;
0248 int output_debug_info = 0;
0249
0250 ret = (*jvmti)->GetMethodDeclaringClass(jvmti, method,
0251 &decl_class);
0252 if (ret != JVMTI_ERROR_NONE) {
0253 print_error(jvmti, "GetMethodDeclaringClass", ret);
0254 return;
0255 }
0256
0257 if (has_line_numbers && map && map_length) {
0258 ret = get_line_numbers(jvmti, compile_info, &line_tab, &nr_lines);
0259 if (ret != JVMTI_ERROR_NONE) {
0260 if (ret != JVMTI_ERROR_NOT_FOUND) {
0261 warnx("jvmti: cannot get line table for method");
0262 }
0263 nr_lines = 0;
0264 } else if (nr_lines > 0) {
0265 line_file_names = malloc(sizeof(char*) * nr_lines);
0266 if (!line_file_names) {
0267 warnx("jvmti: cannot allocate space for line table method names");
0268 } else {
0269 memset(line_file_names, 0, sizeof(char*) * nr_lines);
0270 ret = fill_source_filenames(jvmti, nr_lines, line_tab, line_file_names);
0271 if (ret != JVMTI_ERROR_NONE) {
0272 warnx("jvmti: fill_source_filenames failed");
0273 } else {
0274 output_debug_info = 1;
0275 }
0276 }
0277 }
0278 }
0279
0280 ret = (*jvmti)->GetClassSignature(jvmti, decl_class,
0281 &class_sign, NULL);
0282 if (ret != JVMTI_ERROR_NONE) {
0283 print_error(jvmti, "GetClassSignature", ret);
0284 goto error;
0285 }
0286
0287 ret = (*jvmti)->GetMethodName(jvmti, method, &func_name,
0288 &func_sign, NULL);
0289 if (ret != JVMTI_ERROR_NONE) {
0290 print_error(jvmti, "GetMethodName", ret);
0291 goto error;
0292 }
0293
0294
0295
0296
0297 if (output_debug_info)
0298 if (jvmti_write_debug_info(jvmti_agent, addr, nr_lines, line_tab, (const char * const *) line_file_names))
0299 warnx("jvmti: write_debug_info() failed");
0300
0301 len = strlen(func_name) + strlen(class_sign) + strlen(func_sign) + 2;
0302 {
0303 char str[len];
0304 snprintf(str, len, "%s%s%s", class_sign, func_name, func_sign);
0305
0306 if (jvmti_write_code(jvmti_agent, str, addr, code_addr, code_size))
0307 warnx("jvmti: write_code() failed");
0308 }
0309 error:
0310 (*jvmti)->Deallocate(jvmti, (unsigned char *)func_name);
0311 (*jvmti)->Deallocate(jvmti, (unsigned char *)func_sign);
0312 (*jvmti)->Deallocate(jvmti, (unsigned char *)class_sign);
0313 free(line_tab);
0314 while (line_file_names && (nr_lines > 0)) {
0315 if (line_file_names[nr_lines - 1]) {
0316 free(line_file_names[nr_lines - 1]);
0317 }
0318 nr_lines -= 1;
0319 }
0320 free(line_file_names);
0321 }
0322
0323 static void JNICALL
0324 code_generated_cb(jvmtiEnv *jvmti,
0325 char const *name,
0326 void const *code_addr,
0327 jint code_size)
0328 {
0329 uint64_t addr = (uint64_t)(unsigned long)code_addr;
0330 int ret;
0331
0332 ret = jvmti_write_code(jvmti_agent, name, addr, code_addr, code_size);
0333 if (ret)
0334 warnx("jvmti: write_code() failed for code_generated");
0335 }
0336
0337 JNIEXPORT jint JNICALL
0338 Agent_OnLoad(JavaVM *jvm, char *options, void *reserved __maybe_unused)
0339 {
0340 jvmtiEventCallbacks cb;
0341 jvmtiCapabilities caps1;
0342 jvmtiJlocationFormat format;
0343 jvmtiEnv *jvmti = NULL;
0344 jint ret;
0345
0346 jvmti_agent = jvmti_open();
0347 if (!jvmti_agent) {
0348 warnx("jvmti: open_agent failed");
0349 return -1;
0350 }
0351
0352
0353
0354
0355 ret = (*jvm)->GetEnv(jvm, (void *)&jvmti, JVMTI_VERSION_1);
0356 if (ret != JNI_OK) {
0357 warnx("jvmti: jvmti version 1 not supported");
0358 return -1;
0359 }
0360
0361
0362
0363
0364
0365 memset(&caps1, 0, sizeof(caps1));
0366 caps1.can_generate_compiled_method_load_events = 1;
0367
0368 ret = (*jvmti)->AddCapabilities(jvmti, &caps1);
0369 if (ret != JVMTI_ERROR_NONE) {
0370 print_error(jvmti, "AddCapabilities", ret);
0371 return -1;
0372 }
0373 ret = (*jvmti)->GetJLocationFormat(jvmti, &format);
0374 if (ret == JVMTI_ERROR_NONE && format == JVMTI_JLOCATION_JVMBCI) {
0375 memset(&caps1, 0, sizeof(caps1));
0376 caps1.can_get_line_numbers = 1;
0377 caps1.can_get_source_file_name = 1;
0378 ret = (*jvmti)->AddCapabilities(jvmti, &caps1);
0379 if (ret == JVMTI_ERROR_NONE)
0380 has_line_numbers = 1;
0381 } else if (ret != JVMTI_ERROR_NONE)
0382 print_error(jvmti, "GetJLocationFormat", ret);
0383
0384
0385 memset(&cb, 0, sizeof(cb));
0386
0387 cb.CompiledMethodLoad = compiled_method_load_cb;
0388 cb.DynamicCodeGenerated = code_generated_cb;
0389
0390 ret = (*jvmti)->SetEventCallbacks(jvmti, &cb, sizeof(cb));
0391 if (ret != JVMTI_ERROR_NONE) {
0392 print_error(jvmti, "SetEventCallbacks", ret);
0393 return -1;
0394 }
0395
0396 ret = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
0397 JVMTI_EVENT_COMPILED_METHOD_LOAD, NULL);
0398 if (ret != JVMTI_ERROR_NONE) {
0399 print_error(jvmti, "SetEventNotificationMode(METHOD_LOAD)", ret);
0400 return -1;
0401 }
0402
0403 ret = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE,
0404 JVMTI_EVENT_DYNAMIC_CODE_GENERATED, NULL);
0405 if (ret != JVMTI_ERROR_NONE) {
0406 print_error(jvmti, "SetEventNotificationMode(CODE_GENERATED)", ret);
0407 return -1;
0408 }
0409 return 0;
0410 }
0411
0412 JNIEXPORT void JNICALL
0413 Agent_OnUnload(JavaVM *jvm __maybe_unused)
0414 {
0415 int ret;
0416
0417 ret = jvmti_close(jvmti_agent);
0418 if (ret)
0419 errx(1, "Error: op_close_agent()");
0420 }