linux/tools/perf/jvmti/jvmti_agent.c
<<
>>
Prefs
   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
  45static char jit_path[PATH_MAX];
  46static void *marker_addr;
  47
  48#ifndef HAVE_GETTID
  49static inline pid_t gettid(void)
  50{
  51        return (pid_t)syscall(__NR_gettid);
  52}
  53#endif
  54
  55static 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;
  83error:
  84        close(fd);
  85        return ret;
  86}
  87
  88static int use_arch_timestamp;
  89
  90static inline uint64_t
  91get_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
 105static int perf_clk_id = CLOCK_MONOTONIC;
 106
 107static inline uint64_t
 108timespec_to_ns(const struct timespec *ts)
 109{
 110        return ((uint64_t) ts->tv_sec * NSEC_PER_SEC) + ts->tv_nsec;
 111}
 112
 113static inline uint64_t
 114perf_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
 129static int
 130create_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
 196static int
 197perf_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
 220static void
 221perf_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
 235static void
 236init_arch_timestamp(void)
 237{
 238        char *str = getenv("JITDUMP_USE_ARCH_TIMESTAMP");
 239
 240        if (!str || !*str || !strcmp(str, "0"))
 241                return;
 242
 243        use_arch_timestamp = 1;
 244}
 245
 246void *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;
 325error:
 326        fclose(fp);
 327        return NULL;
 328}
 329
 330int
 331jvmti_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
 358int
 359jvmti_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
 416int
 417jvmti_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;
 493error:
 494        funlockfile(fp);
 495        return -1;
 496}
 497