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