1 #include <linux/compiler.h> 2 #include <sys/types.h> 3 #include <stdio.h> 4 #include <string.h> 5 #include <stdlib.h> 6 #include <err.h> 7 #include <jvmti.h> 8 #include <jvmticmlr.h> 9 #include <limits.h> 10 11 #include "jvmti_agent.h" 12 13 static int has_line_numbers; 14 void *jvmti_agent; 15 16 static void print_error(jvmtiEnv *jvmti, const char *msg, jvmtiError ret) 17 { 18 char *err_msg = NULL; 19 jvmtiError err; 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); 24 } else { 25 warnx("%s failed with an unknown error %d", msg, ret); 26 } 27 } 28 29 static jvmtiError 30 do_get_line_numbers(jvmtiEnv *jvmti, void *pc, jmethodID m, jint bci, 31 jvmti_line_info_t *tab, jint *nr) 32 { 33 jint i, lines = 0; 34 jint nr_lines = 0; 35 jvmtiLineNumberEntry *loc_tab = NULL; 36 jvmtiError ret; 37 38 ret = (*jvmti)->GetLineNumberTable(jvmti, m, &nr_lines, &loc_tab); 39 if (ret != JVMTI_ERROR_NONE) { 40 print_error(jvmti, "GetLineNumberTable", ret); 41 return ret; 42 } 43 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 */ 49 lines++; 50 } else { 51 break; 52 } 53 } 54 (*jvmti)->Deallocate(jvmti, (unsigned char *)loc_tab); 55 *nr = lines; 56 return JVMTI_ERROR_NONE; 57 } 58 59 static jvmtiError 60 get_line_numbers(jvmtiEnv *jvmti, const void *compile_info, jvmti_line_info_t **tab, int *nr_lines) 61 { 62 const jvmtiCompiledMethodLoadRecordHeader *hdr; 63 jvmtiCompiledMethodLoadInlineRecord *rec; 64 jvmtiLineNumberEntry *lne = NULL; 65 PCStackInfo *c; 66 jint nr, ret; 67 int nr_total = 0; 68 int i, lines_total = 0; 69 70 if (!(tab && nr_lines)) 71 return JVMTI_ERROR_NULL_POINTER; 72 73 /* 74 * Phase 1 -- get the number of lines necessary 75 */ 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++) { 80 c = rec->pcinfo + i; 81 nr = 0; 82 /* 83 * unfortunately, need a tab to get the number of lines! 84 */ 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); 89 nr_total += (int)nr; 90 } else { 91 print_error(jvmti, "GetLineNumberTable", ret); 92 } 93 } 94 } 95 } 96 97 if (nr_total == 0) 98 return JVMTI_ERROR_NOT_FOUND; 99 100 /* 101 * Phase 2 -- allocate big enough line table 102 */ 103 *tab = malloc(nr_total * sizeof(**tab)); 104 if (!*tab) 105 return JVMTI_ERROR_OUT_OF_MEMORY; 106 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++) { 111 c = rec->pcinfo + i; 112 nr = 0; 113 ret = do_get_line_numbers(jvmti, c->pc, 114 c->methods[0], 115 c->bcis[0], 116 *tab + lines_total, 117 &nr); 118 if (ret == JVMTI_ERROR_NONE) 119 lines_total += nr; 120 } 121 } 122 } 123 *nr_lines = lines_total; 124 return JVMTI_ERROR_NONE; 125 } 126 127 static void JNICALL 128 compiled_method_load_cb(jvmtiEnv *jvmti, 129 jmethodID method, 130 jint code_size, 131 void const *code_addr, 132 jint map_length, 133 jvmtiAddrLocationMap const *map, 134 const void *compile_info) 135 { 136 jvmti_line_info_t *line_tab = NULL; 137 jclass decl_class; 138 char *class_sign = NULL; 139 char *func_name = NULL; 140 char *func_sign = NULL; 141 char *file_name= NULL; 142 char fn[PATH_MAX]; 143 uint64_t addr = (uint64_t)(uintptr_t)code_addr; 144 jvmtiError ret; 145 int nr_lines = 0; /* in line_tab[] */ 146 size_t len; 147 148 ret = (*jvmti)->GetMethodDeclaringClass(jvmti, method, 149 &decl_class); 150 if (ret != JVMTI_ERROR_NONE) { 151 print_error(jvmti, "GetMethodDeclaringClass", ret); 152 return; 153 } 154 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"); 159 nr_lines = 0; 160 } 161 } 162 163 ret = (*jvmti)->GetSourceFileName(jvmti, decl_class, &file_name); 164 if (ret != JVMTI_ERROR_NONE) { 165 print_error(jvmti, "GetSourceFileName", ret); 166 goto error; 167 } 168 169 ret = (*jvmti)->GetClassSignature(jvmti, decl_class, 170 &class_sign, NULL); 171 if (ret != JVMTI_ERROR_NONE) { 172 print_error(jvmti, "GetClassSignature", ret); 173 goto error; 174 } 175 176 ret = (*jvmti)->GetMethodName(jvmti, method, &func_name, 177 &func_sign, NULL); 178 if (ret != JVMTI_ERROR_NONE) { 179 print_error(jvmti, "GetMethodName", ret); 180 goto error; 181 } 182 183 /* 184 * Assume path name is class hierarchy, this is a common practice with Java programs 185 */ 186 if (*class_sign == 'L') { 187 int j, i = 0; 188 char *p = strrchr(class_sign, '/'); 189 if (p) { 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]; 193 } 194 /* 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 197 */ 198 for (j = 0; i < (PATH_MAX - 1) && file_name && j < strlen(file_name); j++, i++) 199 fn[i] = file_name[j]; 200 fn[i] = '\0'; 201 } else { 202 /* fallback case */ 203 strcpy(fn, file_name); 204 } 205 /* 206 * write source line info record if we have it 207 */ 208 if (jvmti_write_debug_info(jvmti_agent, addr, fn, line_tab, nr_lines)) 209 warnx("jvmti: write_debug_info() failed"); 210 211 len = strlen(func_name) + strlen(class_sign) + strlen(func_sign) + 2; 212 { 213 char str[len]; 214 snprintf(str, len, "%s%s%s", class_sign, func_name, func_sign); 215 216 if (jvmti_write_code(jvmti_agent, str, addr, code_addr, code_size)) 217 warnx("jvmti: write_code() failed"); 218 } 219 error: 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); 224 free(line_tab); 225 } 226 227 static void JNICALL 228 code_generated_cb(jvmtiEnv *jvmti, 229 char const *name, 230 void const *code_addr, 231 jint code_size) 232 { 233 uint64_t addr = (uint64_t)(unsigned long)code_addr; 234 int ret; 235 236 ret = jvmti_write_code(jvmti_agent, name, addr, code_addr, code_size); 237 if (ret) 238 warnx("jvmti: write_code() failed for code_generated"); 239 } 240 241 JNIEXPORT jint JNICALL 242 Agent_OnLoad(JavaVM *jvm, char *options, void *reserved __maybe_unused) 243 { 244 jvmtiEventCallbacks cb; 245 jvmtiCapabilities caps1; 246 jvmtiJlocationFormat format; 247 jvmtiEnv *jvmti = NULL; 248 jint ret; 249 250 jvmti_agent = jvmti_open(); 251 if (!jvmti_agent) { 252 warnx("jvmti: open_agent failed"); 253 return -1; 254 } 255 256 /* 257 * Request a JVMTI interface version 1 environment 258 */ 259 ret = (*jvm)->GetEnv(jvm, (void *)&jvmti, JVMTI_VERSION_1); 260 if (ret != JNI_OK) { 261 warnx("jvmti: jvmti version 1 not supported"); 262 return -1; 263 } 264 265 /* 266 * acquire method_load capability, we require it 267 * request line numbers (optional) 268 */ 269 memset(&caps1, 0, sizeof(caps1)); 270 caps1.can_generate_compiled_method_load_events = 1; 271 272 ret = (*jvmti)->AddCapabilities(jvmti, &caps1); 273 if (ret != JVMTI_ERROR_NONE) { 274 print_error(jvmti, "AddCapabilities", ret); 275 return -1; 276 } 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); 287 288 289 memset(&cb, 0, sizeof(cb)); 290 291 cb.CompiledMethodLoad = compiled_method_load_cb; 292 cb.DynamicCodeGenerated = code_generated_cb; 293 294 ret = (*jvmti)->SetEventCallbacks(jvmti, &cb, sizeof(cb)); 295 if (ret != JVMTI_ERROR_NONE) { 296 print_error(jvmti, "SetEventCallbacks", ret); 297 return -1; 298 } 299 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); 304 return -1; 305 } 306 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); 311 return -1; 312 } 313 return 0; 314 } 315 316 JNIEXPORT void JNICALL 317 Agent_OnUnload(JavaVM *jvm __maybe_unused) 318 { 319 int ret; 320 321 ret = jvmti_close(jvmti_agent); 322 if (ret) 323 errx(1, "Error: op_close_agent()"); 324 } 325