Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6e7d6fdc authored by Peter Zijlstra's avatar Peter Zijlstra Committed by Ingo Molnar
Browse files

perf report: Add --sort <call> --call <$regex>



Implement sorting by callchain symbols, --sort <call>.

It will create a new column which will show a match to
--call $regex or "[unmatched]".

Signed-off-by: default avatarPeter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Arnaldo Carvalho de Melo <acme@redhat.com>
LKML-Reference: <new-submission>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
parent a3d06cc6
Loading
Loading
Loading
Loading
+158 −51
Original line number Diff line number Diff line
@@ -40,11 +40,13 @@ static int dump_trace = 0;

static int		verbose;
static int		full_paths;
static int		collapse_syscalls;

static unsigned long	page_size;
static unsigned long	mmap_window = 32;

static char		*call = "^sys_";
static regex_t		call_regex;

struct ip_chain_event {
	__u16 nr;
	__u16 hv;
@@ -463,6 +465,7 @@ struct hist_entry {
	struct map	 *map;
	struct dso	 *dso;
	struct symbol	 *sym;
	struct symbol	 *call;
	__u64		 ip;
	char		 level;

@@ -483,6 +486,16 @@ struct sort_entry {
	size_t	(*print)(FILE *fp, struct hist_entry *);
};

static int64_t cmp_null(void *l, void *r)
{
	if (!l && !r)
		return 0;
	else if (!l)
		return -1;
	else
		return 1;
}

/* --sort pid */

static int64_t
@@ -517,14 +530,8 @@ sort__comm_collapse(struct hist_entry *left, struct hist_entry *right)
	char *comm_l = left->thread->comm;
	char *comm_r = right->thread->comm;

	if (!comm_l || !comm_r) {
		if (!comm_l && !comm_r)
			return 0;
		else if (!comm_l)
			return -1;
		else
			return 1;
	}
	if (!comm_l || !comm_r)
		return cmp_null(comm_l, comm_r);

	return strcmp(comm_l, comm_r);
}
@@ -550,14 +557,8 @@ sort__dso_cmp(struct hist_entry *left, struct hist_entry *right)
	struct dso *dso_l = left->dso;
	struct dso *dso_r = right->dso;

	if (!dso_l || !dso_r) {
		if (!dso_l && !dso_r)
			return 0;
		else if (!dso_l)
			return -1;
		else
			return 1;
	}
	if (!dso_l || !dso_r)
		return cmp_null(dso_l, dso_r);

	return strcmp(dso_l->name, dso_r->name);
}
@@ -617,7 +618,38 @@ static struct sort_entry sort_sym = {
	.print	= sort__sym_print,
};

/* --sort call */

static int64_t
sort__call_cmp(struct hist_entry *left, struct hist_entry *right)
{
	struct symbol *sym_l = left->call;
	struct symbol *sym_r = right->call;

	if (!sym_l || !sym_r)
		return cmp_null(sym_l, sym_r);

	return strcmp(sym_l->name, sym_r->name);
}

static size_t
sort__call_print(FILE *fp, struct hist_entry *self)
{
	size_t ret = 0;

	ret += fprintf(fp, "%-20s", self->call ? self->call->name : "[unmatched]");

	return ret;
}

static struct sort_entry sort_call = {
	.header = "Callchain symbol    ",
	.cmp	= sort__call_cmp,
	.print	= sort__call_print,
};

static int sort__need_collapse = 0;
static int sort__has_call = 0;

struct sort_dimension {
	char			*name;
@@ -630,6 +662,7 @@ static struct sort_dimension sort_dimensions[] = {
	{ .name = "comm",	.entry = &sort_comm,	},
	{ .name = "dso",	.entry = &sort_dso,	},
	{ .name = "symbol",	.entry = &sort_sym,	},
	{ .name = "call",	.entry = &sort_call,	},
};

static LIST_HEAD(hist_entry__sort_list);
@@ -650,6 +683,18 @@ static int sort_dimension__add(char *tok)
		if (sd->entry->collapse)
			sort__need_collapse = 1;

		if (sd->entry == &sort_call) {
			int ret = regcomp(&call_regex, call, REG_EXTENDED);
			if (ret) {
				char err[BUFSIZ];

				regerror(ret, &call_regex, err, sizeof(err));
				fprintf(stderr, "Invalid regex: %s\n%s", call, err);
				exit(-1);
			}
			sort__has_call = 1;
		}

		list_add_tail(&sd->entry->list, &hist_entry__sort_list);
		sd->taken = 1;

@@ -730,13 +775,76 @@ hist_entry__fprintf(FILE *fp, struct hist_entry *self, __u64 total_samples)
	return ret;
}

/*
 *
 */

static struct symbol *
resolve_symbol(struct thread *thread, struct map **mapp,
	       struct dso **dsop, __u64 *ipp)
{
	struct dso *dso = dsop ? *dsop : NULL;
	struct map *map = mapp ? *mapp : NULL;
	uint64_t ip = *ipp;

	if (!thread)
		return NULL;

	if (dso)
		goto got_dso;

	if (map)
		goto got_map;

	map = thread__find_map(thread, ip);
	if (map != NULL) {
		if (mapp)
			*mapp = map;
got_map:
		ip = map->map_ip(map, ip);
		*ipp  = ip;

		dso = map->dso;
	} else {
		/*
		 * If this is outside of all known maps,
		 * and is a negative address, try to look it
		 * up in the kernel dso, as it might be a
		 * vsyscall (which executes in user-mode):
		 */
		if ((long long)ip < 0)
		dso = kernel_dso;
	}
	dprintf(" ...... dso: %s\n", dso ? dso->name : "<not found>");

	if (dsop)
		*dsop = dso;

	if (!dso)
		return NULL;
got_dso:
	return dso->find_symbol(dso, ip);
}

static struct symbol *call__match(struct symbol *sym)
{
	if (!sym)
		return NULL;

	if (sym->name && !regexec(&call_regex, sym->name, 0, NULL, 0))
		return sym;

	return NULL;
}

/*
 * collect histogram counts
 */

static int
hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,
		struct symbol *sym, __u64 ip, char level, __u64 count)
		struct symbol *sym, __u64 ip, struct ip_chain_event *chain,
	       	char level, __u64 count)
{
	struct rb_node **p = &hist.rb_node;
	struct rb_node *parent = NULL;
@@ -752,6 +860,33 @@ hist_entry__add(struct thread *thread, struct map *map, struct dso *dso,
	};
	int cmp;

	if (sort__has_call && chain) {
		int i, nr = chain->hv;
		struct symbol *sym;
		struct dso *dso;
		__u64 ip;

		for (i = 0; i < chain->kernel; i++) {
			ip = chain->ips[nr + i];
			dso = kernel_dso;
			sym = resolve_symbol(thread, NULL, &dso, &ip);
			entry.call = call__match(sym);
			if (entry.call)
				goto got_call;
		}
		nr += i;

		for (i = 0; i < chain->user; i++) {
			ip = chain->ips[nr + i];
			sym = resolve_symbol(thread, NULL, NULL, &ip);
			entry.call = call__match(sym);
			if (entry.call)
				goto got_call;
		}
		nr += i;
	}
got_call:

	while (*p != NULL) {
		parent = *p;
		he = rb_entry(parent, struct hist_entry, rb_node);
@@ -955,7 +1090,7 @@ process_overflow_event(event_t *event, unsigned long offset, unsigned long head)
	__u64 period = 1;
	struct map *map = NULL;
	void *more_data = event->ip.__more_data;
	struct ip_chain_event *chain;
	struct ip_chain_event *chain = NULL;

	if (event->header.type & PERF_SAMPLE_PERIOD) {
		period = *(__u64 *)more_data;
@@ -984,15 +1119,6 @@ process_overflow_event(event_t *event, unsigned long offset, unsigned long head)
			for (i = 0; i < chain->nr; i++)
				dprintf("..... %2d: %016Lx\n", i, chain->ips[i]);
		}
		if (collapse_syscalls) {
			/*
			 * Find the all-but-last kernel entry
			 * amongst the call-chains - to get
			 * to the level of system calls:
			 */
			if (chain->kernel >= 2)
				ip = chain->ips[chain->kernel-2];
		}
	}

	dprintf(" ... thread: %s:%d\n", thread->comm, thread->pid);
@@ -1016,22 +1142,6 @@ process_overflow_event(event_t *event, unsigned long offset, unsigned long head)
		show = SHOW_USER;
		level = '.';

		map = thread__find_map(thread, ip);
		if (map != NULL) {
			ip = map->map_ip(map, ip);
			dso = map->dso;
		} else {
			/*
			 * If this is outside of all known maps,
			 * and is a negative address, try to look it
			 * up in the kernel dso, as it might be a
			 * vsyscall (which executes in user-mode):
			 */
			if ((long long)ip < 0)
				dso = kernel_dso;
		}
		dprintf(" ...... dso: %s\n", dso ? dso->name : "<not found>");

	} else {
		show = SHOW_HV;
		level = 'H';
@@ -1039,12 +1149,9 @@ process_overflow_event(event_t *event, unsigned long offset, unsigned long head)
	}

	if (show & show_mask) {
		struct symbol *sym = NULL;

		if (dso)
			sym = dso->find_symbol(dso, ip);
		struct symbol *sym = resolve_symbol(thread, &map, &dso, &ip);

		if (hist_entry__add(thread, map, dso, sym, ip, level, period)) {
		if (hist_entry__add(thread, map, dso, sym, ip, chain, level, period)) {
			fprintf(stderr,
		"problem incrementing symbol count, skipping event\n");
			return -1;
@@ -1353,8 +1460,8 @@ static const struct option options[] = {
		   "sort by key(s): pid, comm, dso, symbol. Default: pid,symbol"),
	OPT_BOOLEAN('P', "full-paths", &full_paths,
		    "Don't shorten the pathnames taking into account the cwd"),
	OPT_BOOLEAN('S', "syscalls", &collapse_syscalls,
		    "show per syscall summary overhead, using call graph"),
	OPT_STRING('c', "call", &call, "regex",
		   "regex to use for --sort call"),
	OPT_END()
};