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
  39#include "jvmti_agent.h"
  40#include "../util/jitdump.h"
  41
  42#define JIT_LANG "java"
  43
  44static char jit_path[PATH_MAX];
  45static void *marker_addr;
  46
  47/*
  48 * padding buffer
  49 */
  50static const char pad_bytes[7];
  51
  52static inline pid_t gettid(void)
  53{
  54        return (pid_t)syscall(__NR_gettid);
  55}
  56
  57static 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;
  90error:
  91        close(fd);
  92        return ret;
  93}
  94
  95#define NSEC_PER_SEC    1000000000
  96static int perf_clk_id = CLOCK_MONOTONIC;
  97
  98static inline uint64_t
  99timespec_to_ns(const struct timespec *ts)
 100{
 101        return ((uint64_t) ts->tv_sec * NSEC_PER_SEC) + ts->tv_nsec;
 102}
 103
 104static inline uint64_t
 105perf_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
 117static int
 118debug_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
 167static int
 168perf_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
 191static void
 192perf_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
 206void *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;
 278error:
 279        fclose(fp);
 280        return NULL;
 281}
 282
 283int
 284jvmti_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
 311int
 312jvmti_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
 375int
 376jvmti_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;
 462error:
 463        funlockfile(fp);
 464        return -1;
 465}
 466