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