1 /* 2 * jvmti_agent.c: JVMTI agent interface 3 * 4 * Adapted from the Oprofile code in opagent.c: 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Lesser General Public 7 * License as published by the Free Software Foundation; either 8 * version 2.1 of the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Lesser General Public License for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public 16 * License along with this library; if not, write to the Free Software 17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 * 19 * Copyright 2007 OProfile authors 20 * Jens Wilke 21 * Daniel Hansel 22 * Copyright IBM Corporation 2007 23 */ 24 #include <sys/types.h> 25 #include <sys/stat.h> /* for mkdir() */ 26 #include <stdio.h> 27 #include <errno.h> 28 #include <string.h> 29 #include <stdlib.h> 30 #include <stdint.h> 31 #include <limits.h> 32 #include <fcntl.h> 33 #include <unistd.h> 34 #include <time.h> 35 #include <sys/mman.h> 36 #include <syscall.h> /* for gettid() */ 37 #include <err.h> 38 39 #include "jvmti_agent.h" 40 #include "../util/jitdump.h" 41 42 #define JIT_LANG "java" 43 44 static char jit_path[PATH_MAX]; 45 static void *marker_addr; 46 47 static inline pid_t gettid(void) 48 { 49 return (pid_t)syscall(__NR_gettid); 50 } 51 52 static int get_e_machine(struct jitheader *hdr) 53 { 54 ssize_t sret; 55 char id[16]; 56 int fd, ret = -1; 57 struct { 58 uint16_t e_type; 59 uint16_t e_machine; 60 } info; 61 62 fd = open("/proc/self/exe", O_RDONLY); 63 if (fd == -1) 64 return -1; 65 66 sret = read(fd, id, sizeof(id)); 67 if (sret != sizeof(id)) 68 goto error; 69 70 /* check ELF signature */ 71 if (id[0] != 0x7f || id[1] != 'E' || id[2] != 'L' || id[3] != 'F') 72 goto error; 73 74 sret = read(fd, &info, sizeof(info)); 75 if (sret != sizeof(info)) 76 goto error; 77 78 hdr->elf_mach = info.e_machine; 79 ret = 0; 80 error: 81 close(fd); 82 return ret; 83 } 84 85 static int use_arch_timestamp; 86 87 static inline uint64_t 88 get_arch_timestamp(void) 89 { 90 #if defined(__i386__) || defined(__x86_64__) 91 unsigned int low, high; 92 93 asm volatile("rdtsc" : "=a" (low), "=d" (high)); 94 95 return low | ((uint64_t)high) << 32; 96 #else 97 return 0; 98 #endif 99 } 100 101 #define NSEC_PER_SEC 1000000000 102 static int perf_clk_id = CLOCK_MONOTONIC; 103 104 static inline uint64_t 105 timespec_to_ns(const struct timespec *ts) 106 { 107 return ((uint64_t) ts->tv_sec * NSEC_PER_SEC) + ts->tv_nsec; 108 } 109 110 static inline uint64_t 111 perf_get_timestamp(void) 112 { 113 struct timespec ts; 114 int ret; 115 116 if (use_arch_timestamp) 117 return get_arch_timestamp(); 118 119 ret = clock_gettime(perf_clk_id, &ts); 120 if (ret) 121 return 0; 122 123 return timespec_to_ns(&ts); 124 } 125 126 static int 127 debug_cache_init(void) 128 { 129 char str[32]; 130 char *base, *p; 131 struct tm tm; 132 time_t t; 133 int ret; 134 135 time(&t); 136 localtime_r(&t, &tm); 137 138 base = getenv("JITDUMPDIR"); 139 if (!base) 140 base = getenv("HOME"); 141 if (!base) 142 base = "."; 143 144 strftime(str, sizeof(str), JIT_LANG"-jit-%Y%m%d", &tm); 145 146 snprintf(jit_path, PATH_MAX - 1, "%s/.debug/", base); 147 148 ret = mkdir(jit_path, 0755); 149 if (ret == -1) { 150 if (errno != EEXIST) { 151 warn("jvmti: cannot create jit cache dir %s", jit_path); 152 return -1; 153 } 154 } 155 156 snprintf(jit_path, PATH_MAX - 1, "%s/.debug/jit", base); 157 ret = mkdir(jit_path, 0755); 158 if (ret == -1) { 159 if (errno != EEXIST) { 160 warn("cannot create jit cache dir %s", jit_path); 161 return -1; 162 } 163 } 164 165 snprintf(jit_path, PATH_MAX - 1, "%s/.debug/jit/%s.XXXXXXXX", base, str); 166 167 p = mkdtemp(jit_path); 168 if (p != jit_path) { 169 warn("cannot create jit cache dir %s", jit_path); 170 return -1; 171 } 172 173 return 0; 174 } 175 176 static int 177 perf_open_marker_file(int fd) 178 { 179 long pgsz; 180 181 pgsz = sysconf(_SC_PAGESIZE); 182 if (pgsz == -1) 183 return -1; 184 185 /* 186 * we mmap the jitdump to create an MMAP RECORD in perf.data file. 187 * The mmap is captured either live (perf record running when we mmap) 188 * or in deferred mode, via /proc/PID/maps 189 * the MMAP record is used as a marker of a jitdump file for more meta 190 * data info about the jitted code. Perf report/annotate detect this 191 * special filename and process the jitdump file. 192 * 193 * mapping must be PROT_EXEC to ensure it is captured by perf record 194 * even when not using -d option 195 */ 196 marker_addr = mmap(NULL, pgsz, PROT_READ|PROT_EXEC, MAP_PRIVATE, fd, 0); 197 return (marker_addr == MAP_FAILED) ? -1 : 0; 198 } 199 200 static void 201 perf_close_marker_file(void) 202 { 203 long pgsz; 204 205 if (!marker_addr) 206 return; 207 208 pgsz = sysconf(_SC_PAGESIZE); 209 if (pgsz == -1) 210 return; 211 212 munmap(marker_addr, pgsz); 213 } 214 215 static void 216 init_arch_timestamp(void) 217 { 218 char *str = getenv("JITDUMP_USE_ARCH_TIMESTAMP"); 219 220 if (!str || !*str || !strcmp(str, "0")) 221 return; 222 223 use_arch_timestamp = 1; 224 } 225 226 void *jvmti_open(void) 227 { 228 char dump_path[PATH_MAX]; 229 struct jitheader header; 230 int fd; 231 FILE *fp; 232 233 init_arch_timestamp(); 234 235 /* 236 * check if clockid is supported 237 */ 238 if (!perf_get_timestamp()) { 239 if (use_arch_timestamp) 240 warnx("jvmti: arch timestamp not supported"); 241 else 242 warnx("jvmti: kernel does not support %d clock id", perf_clk_id); 243 } 244 245 memset(&header, 0, sizeof(header)); 246 247 debug_cache_init(); 248 249 /* 250 * jitdump file name 251 */ 252 snprintf(dump_path, PATH_MAX, "%s/jit-%i.dump", jit_path, getpid()); 253 254 fd = open(dump_path, O_CREAT|O_TRUNC|O_RDWR, 0666); 255 if (fd == -1) 256 return NULL; 257 258 /* 259 * create perf.data maker for the jitdump file 260 */ 261 if (perf_open_marker_file(fd)) { 262 warnx("jvmti: failed to create marker file"); 263 return NULL; 264 } 265 266 fp = fdopen(fd, "w+"); 267 if (!fp) { 268 warn("jvmti: cannot create %s", dump_path); 269 close(fd); 270 goto error; 271 } 272 273 warnx("jvmti: jitdump in %s", dump_path); 274 275 if (get_e_machine(&header)) { 276 warn("get_e_machine failed\n"); 277 goto error; 278 } 279 280 header.magic = JITHEADER_MAGIC; 281 header.version = JITHEADER_VERSION; 282 header.total_size = sizeof(header); 283 header.pid = getpid(); 284 285 header.timestamp = perf_get_timestamp(); 286 287 if (use_arch_timestamp) 288 header.flags |= JITDUMP_FLAGS_ARCH_TIMESTAMP; 289 290 if (!fwrite(&header, sizeof(header), 1, fp)) { 291 warn("jvmti: cannot write dumpfile header"); 292 goto error; 293 } 294 return fp; 295 error: 296 fclose(fp); 297 return NULL; 298 } 299 300 int 301 jvmti_close(void *agent) 302 { 303 struct jr_code_close rec; 304 FILE *fp = agent; 305 306 if (!fp) { 307 warnx("jvmti: invalid fd in close_agent"); 308 return -1; 309 } 310 311 rec.p.id = JIT_CODE_CLOSE; 312 rec.p.total_size = sizeof(rec); 313 314 rec.p.timestamp = perf_get_timestamp(); 315 316 if (!fwrite(&rec, sizeof(rec), 1, fp)) 317 return -1; 318 319 fclose(fp); 320 321 fp = NULL; 322 323 perf_close_marker_file(); 324 325 return 0; 326 } 327 328 int 329 jvmti_write_code(void *agent, char const *sym, 330 uint64_t vma, void const *code, unsigned int const size) 331 { 332 static int code_generation = 1; 333 struct jr_code_load rec; 334 size_t sym_len; 335 FILE *fp = agent; 336 int ret = -1; 337 338 /* don't care about 0 length function, no samples */ 339 if (size == 0) 340 return 0; 341 342 if (!fp) { 343 warnx("jvmti: invalid fd in write_native_code"); 344 return -1; 345 } 346 347 sym_len = strlen(sym) + 1; 348 349 rec.p.id = JIT_CODE_LOAD; 350 rec.p.total_size = sizeof(rec) + sym_len; 351 rec.p.timestamp = perf_get_timestamp(); 352 353 rec.code_size = size; 354 rec.vma = vma; 355 rec.code_addr = vma; 356 rec.pid = getpid(); 357 rec.tid = gettid(); 358 359 if (code) 360 rec.p.total_size += size; 361 362 /* 363 * If JVM is multi-threaded, nultiple concurrent calls to agent 364 * may be possible, so protect file writes 365 */ 366 flockfile(fp); 367 368 /* 369 * get code index inside lock to avoid race condition 370 */ 371 rec.code_index = code_generation++; 372 373 ret = fwrite_unlocked(&rec, sizeof(rec), 1, fp); 374 fwrite_unlocked(sym, sym_len, 1, fp); 375 376 if (code) 377 fwrite_unlocked(code, size, 1, fp); 378 379 funlockfile(fp); 380 381 ret = 0; 382 383 return ret; 384 } 385 386 int 387 jvmti_write_debug_info(void *agent, uint64_t code, 388 int nr_lines, jvmti_line_info_t *li, 389 const char * const * file_names) 390 { 391 struct jr_code_debug_info rec; 392 size_t sret, len, size, flen = 0; 393 uint64_t addr; 394 FILE *fp = agent; 395 int i; 396 397 /* 398 * no entry to write 399 */ 400 if (!nr_lines) 401 return 0; 402 403 if (!fp) { 404 warnx("jvmti: invalid fd in write_debug_info"); 405 return -1; 406 } 407 408 for (i = 0; i < nr_lines; ++i) { 409 flen += strlen(file_names[i]) + 1; 410 } 411 412 rec.p.id = JIT_CODE_DEBUG_INFO; 413 size = sizeof(rec); 414 rec.p.timestamp = perf_get_timestamp(); 415 rec.code_addr = (uint64_t)(uintptr_t)code; 416 rec.nr_entry = nr_lines; 417 418 /* 419 * on disk source line info layout: 420 * uint64_t : addr 421 * int : line number 422 * int : column discriminator 423 * file[] : source file name 424 */ 425 size += nr_lines * sizeof(struct debug_entry); 426 size += flen; 427 rec.p.total_size = size; 428 429 /* 430 * If JVM is multi-threaded, nultiple concurrent calls to agent 431 * may be possible, so protect file writes 432 */ 433 flockfile(fp); 434 435 sret = fwrite_unlocked(&rec, sizeof(rec), 1, fp); 436 if (sret != 1) 437 goto error; 438 439 for (i = 0; i < nr_lines; i++) { 440 441 addr = (uint64_t)li[i].pc; 442 len = sizeof(addr); 443 sret = fwrite_unlocked(&addr, len, 1, fp); 444 if (sret != 1) 445 goto error; 446 447 len = sizeof(li[0].line_number); 448 sret = fwrite_unlocked(&li[i].line_number, len, 1, fp); 449 if (sret != 1) 450 goto error; 451 452 len = sizeof(li[0].discrim); 453 sret = fwrite_unlocked(&li[i].discrim, len, 1, fp); 454 if (sret != 1) 455 goto error; 456 457 sret = fwrite_unlocked(file_names[i], strlen(file_names[i]) + 1, 1, fp); 458 if (sret != 1) 459 goto error; 460 } 461 funlockfile(fp); 462 return 0; 463 error: 464 funlockfile(fp); 465 return -1; 466 } 467