162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * jvmti_agent.c: JVMTI agent interface
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * Adapted from the Oprofile code in opagent.c:
562306a36Sopenharmony_ci * This library is free software; you can redistribute it and/or
662306a36Sopenharmony_ci * modify it under the terms of the GNU Lesser General Public
762306a36Sopenharmony_ci * License as published by the Free Software Foundation; either
862306a36Sopenharmony_ci * version 2.1 of the License, or (at your option) any later version.
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci * This library is distributed in the hope that it will be useful,
1162306a36Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
1262306a36Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
1362306a36Sopenharmony_ci * Lesser General Public License for more details.
1462306a36Sopenharmony_ci *
1562306a36Sopenharmony_ci * You should have received a copy of the GNU Lesser General Public
1662306a36Sopenharmony_ci * License along with this library; if not, write to the Free Software
1762306a36Sopenharmony_ci * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
1862306a36Sopenharmony_ci *
1962306a36Sopenharmony_ci * Copyright 2007 OProfile authors
2062306a36Sopenharmony_ci * Jens Wilke
2162306a36Sopenharmony_ci * Daniel Hansel
2262306a36Sopenharmony_ci * Copyright IBM Corporation 2007
2362306a36Sopenharmony_ci */
2462306a36Sopenharmony_ci#include <sys/types.h>
2562306a36Sopenharmony_ci#include <sys/stat.h> /* for mkdir() */
2662306a36Sopenharmony_ci#include <stdio.h>
2762306a36Sopenharmony_ci#include <errno.h>
2862306a36Sopenharmony_ci#include <string.h>
2962306a36Sopenharmony_ci#include <stdlib.h>
3062306a36Sopenharmony_ci#include <stdint.h>
3162306a36Sopenharmony_ci#include <limits.h>
3262306a36Sopenharmony_ci#include <fcntl.h>
3362306a36Sopenharmony_ci#include <unistd.h>
3462306a36Sopenharmony_ci#include <time.h>
3562306a36Sopenharmony_ci#include <sys/mman.h>
3662306a36Sopenharmony_ci#include <syscall.h> /* for gettid() */
3762306a36Sopenharmony_ci#include <err.h>
3862306a36Sopenharmony_ci#include <linux/kernel.h>
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci#include "jvmti_agent.h"
4162306a36Sopenharmony_ci#include "../util/jitdump.h"
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci#define JIT_LANG "java"
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic char jit_path[PATH_MAX];
4662306a36Sopenharmony_cistatic void *marker_addr;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci#ifndef HAVE_GETTID
4962306a36Sopenharmony_cistatic inline pid_t gettid(void)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	return (pid_t)syscall(__NR_gettid);
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ci#endif
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic int get_e_machine(struct jitheader *hdr)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	ssize_t sret;
5862306a36Sopenharmony_ci	char id[16];
5962306a36Sopenharmony_ci	int fd, ret = -1;
6062306a36Sopenharmony_ci	struct {
6162306a36Sopenharmony_ci		uint16_t e_type;
6262306a36Sopenharmony_ci		uint16_t e_machine;
6362306a36Sopenharmony_ci	} info;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	fd = open("/proc/self/exe", O_RDONLY);
6662306a36Sopenharmony_ci	if (fd == -1)
6762306a36Sopenharmony_ci		return -1;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	sret = read(fd, id, sizeof(id));
7062306a36Sopenharmony_ci	if (sret != sizeof(id))
7162306a36Sopenharmony_ci		goto error;
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	/* check ELF signature */
7462306a36Sopenharmony_ci	if (id[0] != 0x7f || id[1] != 'E' || id[2] != 'L' || id[3] != 'F')
7562306a36Sopenharmony_ci		goto error;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	sret = read(fd, &info, sizeof(info));
7862306a36Sopenharmony_ci	if (sret != sizeof(info))
7962306a36Sopenharmony_ci		goto error;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	hdr->elf_mach = info.e_machine;
8262306a36Sopenharmony_ci	ret = 0;
8362306a36Sopenharmony_cierror:
8462306a36Sopenharmony_ci	close(fd);
8562306a36Sopenharmony_ci	return ret;
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic int use_arch_timestamp;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic inline uint64_t
9162306a36Sopenharmony_ciget_arch_timestamp(void)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci#if defined(__i386__) || defined(__x86_64__)
9462306a36Sopenharmony_ci	unsigned int low, high;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	asm volatile("rdtsc" : "=a" (low), "=d" (high));
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	return low | ((uint64_t)high) << 32;
9962306a36Sopenharmony_ci#else
10062306a36Sopenharmony_ci	return 0;
10162306a36Sopenharmony_ci#endif
10262306a36Sopenharmony_ci}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci#define NSEC_PER_SEC	1000000000
10562306a36Sopenharmony_cistatic int perf_clk_id = CLOCK_MONOTONIC;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_cistatic inline uint64_t
10862306a36Sopenharmony_citimespec_to_ns(const struct timespec *ts)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci        return ((uint64_t) ts->tv_sec * NSEC_PER_SEC) + ts->tv_nsec;
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic inline uint64_t
11462306a36Sopenharmony_ciperf_get_timestamp(void)
11562306a36Sopenharmony_ci{
11662306a36Sopenharmony_ci	struct timespec ts;
11762306a36Sopenharmony_ci	int ret;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	if (use_arch_timestamp)
12062306a36Sopenharmony_ci		return get_arch_timestamp();
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	ret = clock_gettime(perf_clk_id, &ts);
12362306a36Sopenharmony_ci	if (ret)
12462306a36Sopenharmony_ci		return 0;
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	return timespec_to_ns(&ts);
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic int
13062306a36Sopenharmony_cicreate_jit_cache_dir(void)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	char str[32];
13362306a36Sopenharmony_ci	char *base, *p;
13462306a36Sopenharmony_ci	struct tm tm;
13562306a36Sopenharmony_ci	time_t t;
13662306a36Sopenharmony_ci	int ret;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	time(&t);
13962306a36Sopenharmony_ci	localtime_r(&t, &tm);
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	base = getenv("JITDUMPDIR");
14262306a36Sopenharmony_ci	if (!base)
14362306a36Sopenharmony_ci		base = getenv("HOME");
14462306a36Sopenharmony_ci	if (!base)
14562306a36Sopenharmony_ci		base = ".";
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	strftime(str, sizeof(str), JIT_LANG"-jit-%Y%m%d", &tm);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	ret = snprintf(jit_path, PATH_MAX, "%s/.debug/", base);
15062306a36Sopenharmony_ci	if (ret >= PATH_MAX) {
15162306a36Sopenharmony_ci		warnx("jvmti: cannot generate jit cache dir because %s/.debug/"
15262306a36Sopenharmony_ci			" is too long, please check the cwd, JITDUMPDIR, and"
15362306a36Sopenharmony_ci			" HOME variables", base);
15462306a36Sopenharmony_ci		return -1;
15562306a36Sopenharmony_ci	}
15662306a36Sopenharmony_ci	ret = mkdir(jit_path, 0755);
15762306a36Sopenharmony_ci	if (ret == -1) {
15862306a36Sopenharmony_ci		if (errno != EEXIST) {
15962306a36Sopenharmony_ci			warn("jvmti: cannot create jit cache dir %s", jit_path);
16062306a36Sopenharmony_ci			return -1;
16162306a36Sopenharmony_ci		}
16262306a36Sopenharmony_ci	}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	ret = snprintf(jit_path, PATH_MAX, "%s/.debug/jit", base);
16562306a36Sopenharmony_ci	if (ret >= PATH_MAX) {
16662306a36Sopenharmony_ci		warnx("jvmti: cannot generate jit cache dir because"
16762306a36Sopenharmony_ci			" %s/.debug/jit is too long, please check the cwd,"
16862306a36Sopenharmony_ci			" JITDUMPDIR, and HOME variables", base);
16962306a36Sopenharmony_ci		return -1;
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci	ret = mkdir(jit_path, 0755);
17262306a36Sopenharmony_ci	if (ret == -1) {
17362306a36Sopenharmony_ci		if (errno != EEXIST) {
17462306a36Sopenharmony_ci			warn("jvmti: cannot create jit cache dir %s", jit_path);
17562306a36Sopenharmony_ci			return -1;
17662306a36Sopenharmony_ci		}
17762306a36Sopenharmony_ci	}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	ret = snprintf(jit_path, PATH_MAX, "%s/.debug/jit/%s.XXXXXXXX", base, str);
18062306a36Sopenharmony_ci	if (ret >= PATH_MAX) {
18162306a36Sopenharmony_ci		warnx("jvmti: cannot generate jit cache dir because"
18262306a36Sopenharmony_ci			" %s/.debug/jit/%s.XXXXXXXX is too long, please check"
18362306a36Sopenharmony_ci			" the cwd, JITDUMPDIR, and HOME variables",
18462306a36Sopenharmony_ci			base, str);
18562306a36Sopenharmony_ci		return -1;
18662306a36Sopenharmony_ci	}
18762306a36Sopenharmony_ci	p = mkdtemp(jit_path);
18862306a36Sopenharmony_ci	if (p != jit_path) {
18962306a36Sopenharmony_ci		warn("jvmti: cannot create jit cache dir %s", jit_path);
19062306a36Sopenharmony_ci		return -1;
19162306a36Sopenharmony_ci	}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	return 0;
19462306a36Sopenharmony_ci}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cistatic int
19762306a36Sopenharmony_ciperf_open_marker_file(int fd)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	long pgsz;
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	pgsz = sysconf(_SC_PAGESIZE);
20262306a36Sopenharmony_ci	if (pgsz == -1)
20362306a36Sopenharmony_ci		return -1;
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	/*
20662306a36Sopenharmony_ci	 * we mmap the jitdump to create an MMAP RECORD in perf.data file.
20762306a36Sopenharmony_ci	 * The mmap is captured either live (perf record running when we mmap)
20862306a36Sopenharmony_ci	 * or  in deferred mode, via /proc/PID/maps
20962306a36Sopenharmony_ci	 * the MMAP record is used as a marker of a jitdump file for more meta
21062306a36Sopenharmony_ci	 * data info about the jitted code. Perf report/annotate detect this
21162306a36Sopenharmony_ci	 * special filename and process the jitdump file.
21262306a36Sopenharmony_ci	 *
21362306a36Sopenharmony_ci	 * mapping must be PROT_EXEC to ensure it is captured by perf record
21462306a36Sopenharmony_ci	 * even when not using -d option
21562306a36Sopenharmony_ci	 */
21662306a36Sopenharmony_ci	marker_addr = mmap(NULL, pgsz, PROT_READ|PROT_EXEC, MAP_PRIVATE, fd, 0);
21762306a36Sopenharmony_ci	return (marker_addr == MAP_FAILED) ? -1 : 0;
21862306a36Sopenharmony_ci}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_cistatic void
22162306a36Sopenharmony_ciperf_close_marker_file(void)
22262306a36Sopenharmony_ci{
22362306a36Sopenharmony_ci	long pgsz;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	if (!marker_addr)
22662306a36Sopenharmony_ci		return;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	pgsz = sysconf(_SC_PAGESIZE);
22962306a36Sopenharmony_ci	if (pgsz == -1)
23062306a36Sopenharmony_ci		return;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	munmap(marker_addr, pgsz);
23362306a36Sopenharmony_ci}
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_cistatic void
23662306a36Sopenharmony_ciinit_arch_timestamp(void)
23762306a36Sopenharmony_ci{
23862306a36Sopenharmony_ci	char *str = getenv("JITDUMP_USE_ARCH_TIMESTAMP");
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	if (!str || !*str || !strcmp(str, "0"))
24162306a36Sopenharmony_ci		return;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	use_arch_timestamp = 1;
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_civoid *jvmti_open(void)
24762306a36Sopenharmony_ci{
24862306a36Sopenharmony_ci	char dump_path[PATH_MAX];
24962306a36Sopenharmony_ci	struct jitheader header;
25062306a36Sopenharmony_ci	int fd, ret;
25162306a36Sopenharmony_ci	FILE *fp;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	init_arch_timestamp();
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	/*
25662306a36Sopenharmony_ci	 * check if clockid is supported
25762306a36Sopenharmony_ci	 */
25862306a36Sopenharmony_ci	if (!perf_get_timestamp()) {
25962306a36Sopenharmony_ci		if (use_arch_timestamp)
26062306a36Sopenharmony_ci			warnx("jvmti: arch timestamp not supported");
26162306a36Sopenharmony_ci		else
26262306a36Sopenharmony_ci			warnx("jvmti: kernel does not support %d clock id", perf_clk_id);
26362306a36Sopenharmony_ci	}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	memset(&header, 0, sizeof(header));
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	/*
26862306a36Sopenharmony_ci	 * jitdump file dir
26962306a36Sopenharmony_ci	 */
27062306a36Sopenharmony_ci	if (create_jit_cache_dir() < 0)
27162306a36Sopenharmony_ci		return NULL;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	/*
27462306a36Sopenharmony_ci	 * jitdump file name
27562306a36Sopenharmony_ci	 */
27662306a36Sopenharmony_ci	ret = snprintf(dump_path, PATH_MAX, "%s/jit-%i.dump", jit_path, getpid());
27762306a36Sopenharmony_ci	if (ret >= PATH_MAX) {
27862306a36Sopenharmony_ci		warnx("jvmti: cannot generate jitdump file full path because"
27962306a36Sopenharmony_ci			" %s/jit-%i.dump is too long, please check the cwd,"
28062306a36Sopenharmony_ci			" JITDUMPDIR, and HOME variables", jit_path, getpid());
28162306a36Sopenharmony_ci		return NULL;
28262306a36Sopenharmony_ci	}
28362306a36Sopenharmony_ci
28462306a36Sopenharmony_ci	fd = open(dump_path, O_CREAT|O_TRUNC|O_RDWR, 0666);
28562306a36Sopenharmony_ci	if (fd == -1)
28662306a36Sopenharmony_ci		return NULL;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	/*
28962306a36Sopenharmony_ci	 * create perf.data maker for the jitdump file
29062306a36Sopenharmony_ci	 */
29162306a36Sopenharmony_ci	if (perf_open_marker_file(fd)) {
29262306a36Sopenharmony_ci		warnx("jvmti: failed to create marker file");
29362306a36Sopenharmony_ci		return NULL;
29462306a36Sopenharmony_ci	}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	fp = fdopen(fd, "w+");
29762306a36Sopenharmony_ci	if (!fp) {
29862306a36Sopenharmony_ci		warn("jvmti: cannot create %s", dump_path);
29962306a36Sopenharmony_ci		close(fd);
30062306a36Sopenharmony_ci		goto error;
30162306a36Sopenharmony_ci	}
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	warnx("jvmti: jitdump in %s", dump_path);
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	if (get_e_machine(&header)) {
30662306a36Sopenharmony_ci		warn("get_e_machine failed\n");
30762306a36Sopenharmony_ci		goto error;
30862306a36Sopenharmony_ci	}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_ci	header.magic      = JITHEADER_MAGIC;
31162306a36Sopenharmony_ci	header.version    = JITHEADER_VERSION;
31262306a36Sopenharmony_ci	header.total_size = sizeof(header);
31362306a36Sopenharmony_ci	header.pid        = getpid();
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	header.timestamp = perf_get_timestamp();
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	if (use_arch_timestamp)
31862306a36Sopenharmony_ci		header.flags |= JITDUMP_FLAGS_ARCH_TIMESTAMP;
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci	if (!fwrite(&header, sizeof(header), 1, fp)) {
32162306a36Sopenharmony_ci		warn("jvmti: cannot write dumpfile header");
32262306a36Sopenharmony_ci		goto error;
32362306a36Sopenharmony_ci	}
32462306a36Sopenharmony_ci	return fp;
32562306a36Sopenharmony_cierror:
32662306a36Sopenharmony_ci	fclose(fp);
32762306a36Sopenharmony_ci	return NULL;
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ciint
33162306a36Sopenharmony_cijvmti_close(void *agent)
33262306a36Sopenharmony_ci{
33362306a36Sopenharmony_ci	struct jr_code_close rec;
33462306a36Sopenharmony_ci	FILE *fp = agent;
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci	if (!fp) {
33762306a36Sopenharmony_ci		warnx("jvmti: invalid fd in close_agent");
33862306a36Sopenharmony_ci		return -1;
33962306a36Sopenharmony_ci	}
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci	rec.p.id = JIT_CODE_CLOSE;
34262306a36Sopenharmony_ci	rec.p.total_size = sizeof(rec);
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	rec.p.timestamp = perf_get_timestamp();
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ci	if (!fwrite(&rec, sizeof(rec), 1, fp))
34762306a36Sopenharmony_ci		return -1;
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_ci	fclose(fp);
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	fp = NULL;
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	perf_close_marker_file();
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	return 0;
35662306a36Sopenharmony_ci}
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ciint
35962306a36Sopenharmony_cijvmti_write_code(void *agent, char const *sym,
36062306a36Sopenharmony_ci	uint64_t vma, void const *code, unsigned int const size)
36162306a36Sopenharmony_ci{
36262306a36Sopenharmony_ci	static int code_generation = 1;
36362306a36Sopenharmony_ci	struct jr_code_load rec;
36462306a36Sopenharmony_ci	size_t sym_len;
36562306a36Sopenharmony_ci	FILE *fp = agent;
36662306a36Sopenharmony_ci	int ret = -1;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	/* don't care about 0 length function, no samples */
36962306a36Sopenharmony_ci	if (size == 0)
37062306a36Sopenharmony_ci		return 0;
37162306a36Sopenharmony_ci
37262306a36Sopenharmony_ci	if (!fp) {
37362306a36Sopenharmony_ci		warnx("jvmti: invalid fd in write_native_code");
37462306a36Sopenharmony_ci		return -1;
37562306a36Sopenharmony_ci	}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	sym_len = strlen(sym) + 1;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	rec.p.id           = JIT_CODE_LOAD;
38062306a36Sopenharmony_ci	rec.p.total_size   = sizeof(rec) + sym_len;
38162306a36Sopenharmony_ci	rec.p.timestamp    = perf_get_timestamp();
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	rec.code_size  = size;
38462306a36Sopenharmony_ci	rec.vma        = vma;
38562306a36Sopenharmony_ci	rec.code_addr  = vma;
38662306a36Sopenharmony_ci	rec.pid	       = getpid();
38762306a36Sopenharmony_ci	rec.tid	       = gettid();
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	if (code)
39062306a36Sopenharmony_ci		rec.p.total_size += size;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	/*
39362306a36Sopenharmony_ci	 * If JVM is multi-threaded, multiple concurrent calls to agent
39462306a36Sopenharmony_ci	 * may be possible, so protect file writes
39562306a36Sopenharmony_ci	 */
39662306a36Sopenharmony_ci	flockfile(fp);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	/*
39962306a36Sopenharmony_ci	 * get code index inside lock to avoid race condition
40062306a36Sopenharmony_ci	 */
40162306a36Sopenharmony_ci	rec.code_index = code_generation++;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	ret = fwrite_unlocked(&rec, sizeof(rec), 1, fp);
40462306a36Sopenharmony_ci	fwrite_unlocked(sym, sym_len, 1, fp);
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	if (code)
40762306a36Sopenharmony_ci		fwrite_unlocked(code, size, 1, fp);
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	funlockfile(fp);
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	ret = 0;
41262306a36Sopenharmony_ci
41362306a36Sopenharmony_ci	return ret;
41462306a36Sopenharmony_ci}
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ciint
41762306a36Sopenharmony_cijvmti_write_debug_info(void *agent, uint64_t code,
41862306a36Sopenharmony_ci    int nr_lines, jvmti_line_info_t *li,
41962306a36Sopenharmony_ci    const char * const * file_names)
42062306a36Sopenharmony_ci{
42162306a36Sopenharmony_ci	struct jr_code_debug_info rec;
42262306a36Sopenharmony_ci	size_t sret, len, size, flen = 0;
42362306a36Sopenharmony_ci	uint64_t addr;
42462306a36Sopenharmony_ci	FILE *fp = agent;
42562306a36Sopenharmony_ci	int i;
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	/*
42862306a36Sopenharmony_ci	 * no entry to write
42962306a36Sopenharmony_ci	 */
43062306a36Sopenharmony_ci	if (!nr_lines)
43162306a36Sopenharmony_ci		return 0;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	if (!fp) {
43462306a36Sopenharmony_ci		warnx("jvmti: invalid fd in write_debug_info");
43562306a36Sopenharmony_ci		return -1;
43662306a36Sopenharmony_ci	}
43762306a36Sopenharmony_ci
43862306a36Sopenharmony_ci	for (i = 0; i < nr_lines; ++i) {
43962306a36Sopenharmony_ci	    flen += strlen(file_names[i]) + 1;
44062306a36Sopenharmony_ci	}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_ci	rec.p.id        = JIT_CODE_DEBUG_INFO;
44362306a36Sopenharmony_ci	size            = sizeof(rec);
44462306a36Sopenharmony_ci	rec.p.timestamp = perf_get_timestamp();
44562306a36Sopenharmony_ci	rec.code_addr   = (uint64_t)(uintptr_t)code;
44662306a36Sopenharmony_ci	rec.nr_entry    = nr_lines;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	/*
44962306a36Sopenharmony_ci	 * on disk source line info layout:
45062306a36Sopenharmony_ci	 * uint64_t : addr
45162306a36Sopenharmony_ci	 * int      : line number
45262306a36Sopenharmony_ci	 * int      : column discriminator
45362306a36Sopenharmony_ci	 * file[]   : source file name
45462306a36Sopenharmony_ci	 */
45562306a36Sopenharmony_ci	size += nr_lines * sizeof(struct debug_entry);
45662306a36Sopenharmony_ci	size += flen;
45762306a36Sopenharmony_ci	rec.p.total_size = size;
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci	/*
46062306a36Sopenharmony_ci	 * If JVM is multi-threaded, multiple concurrent calls to agent
46162306a36Sopenharmony_ci	 * may be possible, so protect file writes
46262306a36Sopenharmony_ci	 */
46362306a36Sopenharmony_ci	flockfile(fp);
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	sret = fwrite_unlocked(&rec, sizeof(rec), 1, fp);
46662306a36Sopenharmony_ci	if (sret != 1)
46762306a36Sopenharmony_ci		goto error;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	for (i = 0; i < nr_lines; i++) {
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci		addr = (uint64_t)li[i].pc;
47262306a36Sopenharmony_ci		len  = sizeof(addr);
47362306a36Sopenharmony_ci		sret = fwrite_unlocked(&addr, len, 1, fp);
47462306a36Sopenharmony_ci		if (sret != 1)
47562306a36Sopenharmony_ci			goto error;
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci		len  = sizeof(li[0].line_number);
47862306a36Sopenharmony_ci		sret = fwrite_unlocked(&li[i].line_number, len, 1, fp);
47962306a36Sopenharmony_ci		if (sret != 1)
48062306a36Sopenharmony_ci			goto error;
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci		len  = sizeof(li[0].discrim);
48362306a36Sopenharmony_ci		sret = fwrite_unlocked(&li[i].discrim, len, 1, fp);
48462306a36Sopenharmony_ci		if (sret != 1)
48562306a36Sopenharmony_ci			goto error;
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci		sret = fwrite_unlocked(file_names[i], strlen(file_names[i]) + 1, 1, fp);
48862306a36Sopenharmony_ci		if (sret != 1)
48962306a36Sopenharmony_ci			goto error;
49062306a36Sopenharmony_ci	}
49162306a36Sopenharmony_ci	funlockfile(fp);
49262306a36Sopenharmony_ci	return 0;
49362306a36Sopenharmony_cierror:
49462306a36Sopenharmony_ci	funlockfile(fp);
49562306a36Sopenharmony_ci	return -1;
49662306a36Sopenharmony_ci}
497