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