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 = calloc(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 const 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 = calloc(nr_lines, sizeof(char *)); 266 if (!line_file_names) { 267 warnx("jvmti: cannot allocate space for line table method names"); 268 } else { 269 ret = fill_source_filenames(jvmti, nr_lines, line_tab, line_file_names); 270 if (ret != JVMTI_ERROR_NONE) { 271 warnx("jvmti: fill_source_filenames failed"); 272 } else { 273 output_debug_info = 1; 274 } 275 } 276 } 277 } 278 279 ret = (*jvmti)->GetClassSignature(jvmti, decl_class, 280 &class_sign, NULL); 281 if (ret != JVMTI_ERROR_NONE) { 282 print_error(jvmti, "GetClassSignature", ret); 283 goto error; 284 } 285 286 ret = (*jvmti)->GetMethodName(jvmti, method, &func_name, 287 &func_sign, NULL); 288 if (ret != JVMTI_ERROR_NONE) { 289 print_error(jvmti, "GetMethodName", ret); 290 goto error; 291 } 292 293 /* 294 * write source line info record if we have it 295 */ 296 if (output_debug_info) 297 if (jvmti_write_debug_info(jvmti_agent, addr, nr_lines, line_tab, (const char * const *) line_file_names)) 298 warnx("jvmti: write_debug_info() failed"); 299 300 len = strlen(func_name) + strlen(class_sign) + strlen(func_sign) + 2; 301 { 302 char str[len]; 303 snprintf(str, len, "%s%s%s", class_sign, func_name, func_sign); 304 305 if (jvmti_write_code(jvmti_agent, str, addr, code_addr, code_size)) 306 warnx("jvmti: write_code() failed"); 307 } 308 error: 309 (*jvmti)->Deallocate(jvmti, (unsigned char *)func_name); 310 (*jvmti)->Deallocate(jvmti, (unsigned char *)func_sign); 311 (*jvmti)->Deallocate(jvmti, (unsigned char *)class_sign); 312 free(line_tab); 313 while (line_file_names && (nr_lines > 0)) { 314 if (line_file_names[nr_lines - 1]) { 315 free(line_file_names[nr_lines - 1]); 316 } 317 nr_lines -= 1; 318 } 319 free(line_file_names); 320 } 321 322 static void JNICALL 323 code_generated_cb(jvmtiEnv *jvmti, 324 char const *name, 325 void const *code_addr, 326 jint code_size) 327 { 328 uint64_t addr = (uint64_t)(unsigned long)code_addr; 329 int ret; 330 331 ret = jvmti_write_code(jvmti_agent, name, addr, code_addr, code_size); 332 if (ret) 333 warnx("jvmti: write_code() failed for code_generated"); 334 } 335 336 JNIEXPORT jint JNICALL 337 Agent_OnLoad(JavaVM *jvm, char *options, void *reserved __maybe_unused) 338 { 339 jvmtiEventCallbacks cb; 340 jvmtiCapabilities caps1; 341 jvmtiJlocationFormat format; 342 jvmtiEnv *jvmti = NULL; 343 jint ret; 344 345 jvmti_agent = jvmti_open(); 346 if (!jvmti_agent) { 347 warnx("jvmti: open_agent failed"); 348 return -1; 349 } 350 351 /* 352 * Request a JVMTI interface version 1 environment 353 */ 354 ret = (*jvm)->GetEnv(jvm, (void *)&jvmti, JVMTI_VERSION_1); 355 if (ret != JNI_OK) { 356 warnx("jvmti: jvmti version 1 not supported"); 357 return -1; 358 } 359 360 /* 361 * acquire method_load capability, we require it 362 * request line numbers (optional) 363 */ 364 memset(&caps1, 0, sizeof(caps1)); 365 caps1.can_generate_compiled_method_load_events = 1; 366 367 ret = (*jvmti)->AddCapabilities(jvmti, &caps1); 368 if (ret != JVMTI_ERROR_NONE) { 369 print_error(jvmti, "AddCapabilities", ret); 370 return -1; 371 } 372 ret = (*jvmti)->GetJLocationFormat(jvmti, &format); 373 if (ret == JVMTI_ERROR_NONE && format == JVMTI_JLOCATION_JVMBCI) { 374 memset(&caps1, 0, sizeof(caps1)); 375 caps1.can_get_line_numbers = 1; 376 caps1.can_get_source_file_name = 1; 377 ret = (*jvmti)->AddCapabilities(jvmti, &caps1); 378 if (ret == JVMTI_ERROR_NONE) 379 has_line_numbers = 1; 380 } else if (ret != JVMTI_ERROR_NONE) 381 print_error(jvmti, "GetJLocationFormat", ret); 382 383 384 memset(&cb, 0, sizeof(cb)); 385 386 cb.CompiledMethodLoad = compiled_method_load_cb; 387 cb.DynamicCodeGenerated = code_generated_cb; 388 389 ret = (*jvmti)->SetEventCallbacks(jvmti, &cb, sizeof(cb)); 390 if (ret != JVMTI_ERROR_NONE) { 391 print_error(jvmti, "SetEventCallbacks", ret); 392 return -1; 393 } 394 395 ret = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, 396 JVMTI_EVENT_COMPILED_METHOD_LOAD, NULL); 397 if (ret != JVMTI_ERROR_NONE) { 398 print_error(jvmti, "SetEventNotificationMode(METHOD_LOAD)", ret); 399 return -1; 400 } 401 402 ret = (*jvmti)->SetEventNotificationMode(jvmti, JVMTI_ENABLE, 403 JVMTI_EVENT_DYNAMIC_CODE_GENERATED, NULL); 404 if (ret != JVMTI_ERROR_NONE) { 405 print_error(jvmti, "SetEventNotificationMode(CODE_GENERATED)", ret); 406 return -1; 407 } 408 return 0; 409 } 410 411 JNIEXPORT void JNICALL 412 Agent_OnUnload(JavaVM *jvm __maybe_unused) 413 { 414 int ret; 415 416 ret = jvmti_close(jvmti_agent); 417 if (ret) 418 errx(1, "Error: op_close_agent()"); 419 } 420