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