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 int 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 < (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