brintos

brintos / linux-shallow public Read only

0
0
Text · 10.1 KiB · 526dcaf Raw
497 lines · c
1/*2 * jvmti_agent.c: JVMTI agent interface3 *4 * Adapted from the Oprofile code in opagent.c:5 * This library is free software; you can redistribute it and/or6 * modify it under the terms of the GNU Lesser General Public7 * License as published by the Free Software Foundation; either8 * 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 of12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU13 * Lesser General Public License for more details.14 *15 * You should have received a copy of the GNU Lesser General Public16 * License along with this library; if not, write to the Free Software17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA18 *19 * Copyright 2007 OProfile authors20 * Jens Wilke21 * Daniel Hansel22 * Copyright IBM Corporation 200723 */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_GETTID49static inline pid_t gettid(void)50{51	return (pid_t)syscall(__NR_gettid);52}53#endif54 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_t91get_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#else100	return 0;101#endif102}103 104#define NSEC_PER_SEC	1000000000105static int perf_clk_id = CLOCK_MONOTONIC;106 107static inline uint64_t108timespec_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_t114perf_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 int130create_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 int197perf_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/maps209	 * the MMAP record is used as a marker of a jitdump file for more meta210	 * data info about the jitted code. Perf report/annotate detect this211	 * special filename and process the jitdump file.212	 *213	 * mapping must be PROT_EXEC to ensure it is captured by perf record214	 * even when not using -d option215	 */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 void221perf_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 void236init_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 supported257	 */258	if (!perf_get_timestamp()) {259		if (use_arch_timestamp)260			warnx("jvmti: arch timestamp not supported");261		else262			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 dir269	 */270	if (create_jit_cache_dir() < 0)271		return NULL;272 273	/*274	 * jitdump file name275	 */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 file290	 */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 330int331jvmti_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 358int359jvmti_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 agent394	 * may be possible, so protect file writes395	 */396	flockfile(fp);397 398	/*399	 * get code index inside lock to avoid race condition400	 */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 416int417jvmti_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 write429	 */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 : addr451	 * int      : line number452	 * int      : column discriminator453	 * file[]   : source file name454	 */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 agent461	 * may be possible, so protect file writes462	 */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