3074 lines · 66.5 KB · C
RawDownload
#include "util.h"#include <sys/types.h>#include <byteswap.h>#include <unistd.h>#include <stdio.h>#include <stdlib.h>#include <linux/list.h>#include <linux/kernel.h>#include <linux/bitops.h>#include <sys/utsname.h> #include "evlist.h"#include "evsel.h"#include "header.h"#include "../perf.h"#include "trace-event.h"#include "session.h"#include "symbol.h"#include "debug.h"#include "cpumap.h"#include "pmu.h"#include "vdso.h"#include "strbuf.h"#include "build-id.h"#include "data.h" static bool no_buildid_cache = false; static u32 header_argc;static const char **header_argv; /* * magic2 = "PERFILE2" * must be a numerical value to let the endianness * determine the memory layout. That way we are able * to detect endianness when reading the perf.data file * back. * * we check for legacy (PERFFILE) format. */static const char *__perf_magic1 = "PERFFILE";static const u64 __perf_magic2    = 0x32454c4946524550ULL;static const u64 __perf_magic2_sw = 0x50455246494c4532ULL; #define PERF_MAGIC	__perf_magic2 struct perf_file_attr {	struct perf_event_attr	attr;	struct perf_file_section	ids;}; void perf_header__set_feat(struct perf_header *header, int feat){	set_bit(feat, header->adds_features);} void perf_header__clear_feat(struct perf_header *header, int feat){	clear_bit(feat, header->adds_features);} bool perf_header__has_feat(const struct perf_header *header, int feat){	return test_bit(feat, header->adds_features);} static int do_write(int fd, const void *buf, size_t size){	while (size) {		int ret = write(fd, buf, size); 		if (ret < 0)			return -errno; 		size -= ret;		buf += ret;	} 	return 0;} #define NAME_ALIGN 64 static int write_padded(int fd, const void *bf, size_t count,			size_t count_aligned){	static const char zero_buf[NAME_ALIGN];	int err = do_write(fd, bf, count); 	if (!err)		err = do_write(fd, zero_buf, count_aligned - count); 	return err;} static int do_write_string(int fd, const char *str){	u32 len, olen;	int ret; 	olen = strlen(str) + 1;	len = PERF_ALIGN(olen, NAME_ALIGN); 	/* write len, incl. \0 */	ret = do_write(fd, &len, sizeof(len));	if (ret < 0)		return ret; 	return write_padded(fd, str, olen, len);} static char *do_read_string(int fd, struct perf_header *ph){	ssize_t sz, ret;	u32 len;	char *buf; 	sz = readn(fd, &len, sizeof(len));	if (sz < (ssize_t)sizeof(len))		return NULL; 	if (ph->needs_swap)		len = bswap_32(len); 	buf = malloc(len);	if (!buf)		return NULL; 	ret = readn(fd, buf, len);	if (ret == (ssize_t)len) {		/*		 * strings are padded by zeroes		 * thus the actual strlen of buf		 * may be less than len		 */		return buf;	} 	free(buf);	return NULL;} intperf_header__set_cmdline(int argc, const char **argv){	int i; 	/*	 * If header_argv has already been set, do not override it.	 * This allows a command to set the cmdline, parse args and	 * then call another builtin function that implements a	 * command -- e.g, cmd_kvm calling cmd_record.	 */	if (header_argv)		return 0; 	header_argc = (u32)argc; 	/* do not include NULL termination */	header_argv = calloc(argc, sizeof(char *));	if (!header_argv)		return -ENOMEM; 	/*	 * must copy argv contents because it gets moved	 * around during option parsing	 */	for (i = 0; i < argc ; i++)		header_argv[i] = argv[i]; 	return 0;} #define dsos__for_each_with_build_id(pos, head)	\	list_for_each_entry(pos, head, node)	\		if (!pos->has_build_id)		\			continue;		\		else static int write_buildid(char *name, size_t name_len, u8 *build_id,			 pid_t pid, u16 misc, int fd){	int err;	struct build_id_event b;	size_t len; 	len = name_len + 1;	len = PERF_ALIGN(len, NAME_ALIGN); 	memset(&b, 0, sizeof(b));	memcpy(&b.build_id, build_id, BUILD_ID_SIZE);	b.pid = pid;	b.header.misc = misc;	b.header.size = sizeof(b) + len; 	err = do_write(fd, &b, sizeof(b));	if (err < 0)		return err; 	return write_padded(fd, name, name_len + 1, len);} static int __dsos__write_buildid_table(struct list_head *head,				       struct machine *machine,				       pid_t pid, u16 misc, int fd){	char nm[PATH_MAX];	struct dso *pos; 	dsos__for_each_with_build_id(pos, head) {		int err;		char  *name;		size_t name_len; 		if (!pos->hit)			continue; 		if (is_vdso_map(pos->short_name)) {			name = (char *) VDSO__MAP_NAME;			name_len = sizeof(VDSO__MAP_NAME) + 1;		} else if (dso__is_kcore(pos)) {			machine__mmap_name(machine, nm, sizeof(nm));			name = nm;			name_len = strlen(nm) + 1;		} else {			name = pos->long_name;			name_len = pos->long_name_len + 1;		} 		err = write_buildid(name, name_len, pos->build_id,				    pid, misc, fd);		if (err)			return err;	} 	return 0;} static int machine__write_buildid_table(struct machine *machine, int fd){	int err;	u16 kmisc = PERF_RECORD_MISC_KERNEL,	    umisc = PERF_RECORD_MISC_USER; 	if (!machine__is_host(machine)) {		kmisc = PERF_RECORD_MISC_GUEST_KERNEL;		umisc = PERF_RECORD_MISC_GUEST_USER;	} 	err = __dsos__write_buildid_table(&machine->kernel_dsos, machine,					  machine->pid, kmisc, fd);	if (err == 0)		err = __dsos__write_buildid_table(&machine->user_dsos, machine,						  machine->pid, umisc, fd);	return err;} static int dsos__write_buildid_table(struct perf_header *header, int fd){	struct perf_session *session = container_of(header,			struct perf_session, header);	struct rb_node *nd;	int err = machine__write_buildid_table(&session->machines.host, fd); 	if (err)		return err; 	for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) {		struct machine *pos = rb_entry(nd, struct machine, rb_node);		err = machine__write_buildid_table(pos, fd);		if (err)			break;	}	return err;} int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,			  const char *name, bool is_kallsyms, bool is_vdso){	const size_t size = PATH_MAX;	char *realname, *filename = zalloc(size),	     *linkname = zalloc(size), *targetname;	int len, err = -1;	bool slash = is_kallsyms || is_vdso; 	if (is_kallsyms) {		if (symbol_conf.kptr_restrict) {			pr_debug("Not caching a kptr_restrict'ed /proc/kallsyms\n");			err = 0;			goto out_free;		}		realname = (char *) name;	} else		realname = realpath(name, NULL); 	if (realname == NULL || filename == NULL || linkname == NULL)		goto out_free; 	len = scnprintf(filename, size, "%s%s%s",		       debugdir, slash ? "/" : "",		       is_vdso ? VDSO__MAP_NAME : realname);	if (mkdir_p(filename, 0755))		goto out_free; 	snprintf(filename + len, size - len, "/%s", sbuild_id); 	if (access(filename, F_OK)) {		if (is_kallsyms) {			 if (copyfile("/proc/kallsyms", filename))				goto out_free;		} else if (link(realname, filename) && copyfile(name, filename))			goto out_free;	} 	len = scnprintf(linkname, size, "%s/.build-id/%.2s",		       debugdir, sbuild_id); 	if (access(linkname, X_OK) && mkdir_p(linkname, 0755))		goto out_free; 	snprintf(linkname + len, size - len, "/%s", sbuild_id + 2);	targetname = filename + strlen(debugdir) - 5;	memcpy(targetname, "../..", 5); 	if (symlink(targetname, linkname) == 0)		err = 0;out_free:	if (!is_kallsyms)		free(realname);	free(filename);	free(linkname);	return err;} static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size,				 const char *name, const char *debugdir,				 bool is_kallsyms, bool is_vdso){	char sbuild_id[BUILD_ID_SIZE * 2 + 1]; 	build_id__sprintf(build_id, build_id_size, sbuild_id); 	return build_id_cache__add_s(sbuild_id, debugdir, name,				     is_kallsyms, is_vdso);} int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir){	const size_t size = PATH_MAX;	char *filename = zalloc(size),	     *linkname = zalloc(size);	int err = -1; 	if (filename == NULL || linkname == NULL)		goto out_free; 	snprintf(linkname, size, "%s/.build-id/%.2s/%s",		 debugdir, sbuild_id, sbuild_id + 2); 	if (access(linkname, F_OK))		goto out_free; 	if (readlink(linkname, filename, size - 1) < 0)		goto out_free; 	if (unlink(linkname))		goto out_free; 	/*	 * Since the link is relative, we must make it absolute:	 */	snprintf(linkname, size, "%s/.build-id/%.2s/%s",		 debugdir, sbuild_id, filename); 	if (unlink(linkname))		goto out_free; 	err = 0;out_free:	free(filename);	free(linkname);	return err;} static int dso__cache_build_id(struct dso *dso, struct machine *machine,			       const char *debugdir){	bool is_kallsyms = dso->kernel && dso->long_name[0] != '/';	bool is_vdso = is_vdso_map(dso->short_name);	char *name = dso->long_name;	char nm[PATH_MAX]; 	if (dso__is_kcore(dso)) {		is_kallsyms = true;		machine__mmap_name(machine, nm, sizeof(nm));		name = nm;	}	return build_id_cache__add_b(dso->build_id, sizeof(dso->build_id), name,				     debugdir, is_kallsyms, is_vdso);} static int __dsos__cache_build_ids(struct list_head *head,				   struct machine *machine, const char *debugdir){	struct dso *pos;	int err = 0; 	dsos__for_each_with_build_id(pos, head)		if (dso__cache_build_id(pos, machine, debugdir))			err = -1; 	return err;} static int machine__cache_build_ids(struct machine *machine, const char *debugdir){	int ret = __dsos__cache_build_ids(&machine->kernel_dsos, machine,					  debugdir);	ret |= __dsos__cache_build_ids(&machine->user_dsos, machine, debugdir);	return ret;} static int perf_session__cache_build_ids(struct perf_session *session){	struct rb_node *nd;	int ret;	char debugdir[PATH_MAX]; 	snprintf(debugdir, sizeof(debugdir), "%s", buildid_dir); 	if (mkdir(debugdir, 0755) != 0 && errno != EEXIST)		return -1; 	ret = machine__cache_build_ids(&session->machines.host, debugdir); 	for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) {		struct machine *pos = rb_entry(nd, struct machine, rb_node);		ret |= machine__cache_build_ids(pos, debugdir);	}	return ret ? -1 : 0;} static bool machine__read_build_ids(struct machine *machine, bool with_hits){	bool ret = __dsos__read_build_ids(&machine->kernel_dsos, with_hits);	ret |= __dsos__read_build_ids(&machine->user_dsos, with_hits);	return ret;} static bool perf_session__read_build_ids(struct perf_session *session, bool with_hits){	struct rb_node *nd;	bool ret = machine__read_build_ids(&session->machines.host, with_hits); 	for (nd = rb_first(&session->machines.guests); nd; nd = rb_next(nd)) {		struct machine *pos = rb_entry(nd, struct machine, rb_node);		ret |= machine__read_build_ids(pos, with_hits);	} 	return ret;} static int write_tracing_data(int fd, struct perf_header *h __maybe_unused,			    struct perf_evlist *evlist){	return read_tracing_data(fd, &evlist->entries);}  static int write_build_id(int fd, struct perf_header *h,			  struct perf_evlist *evlist __maybe_unused){	struct perf_session *session;	int err; 	session = container_of(h, struct perf_session, header); 	if (!perf_session__read_build_ids(session, true))		return -1; 	err = dsos__write_buildid_table(h, fd);	if (err < 0) {		pr_debug("failed to write buildid table\n");		return err;	}	if (!no_buildid_cache)		perf_session__cache_build_ids(session); 	return 0;} static int write_hostname(int fd, struct perf_header *h __maybe_unused,			  struct perf_evlist *evlist __maybe_unused){	struct utsname uts;	int ret; 	ret = uname(&uts);	if (ret < 0)		return -1;

Lines 1–500 of 3074.