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

Commit 4b4da7f7 authored by Masami Hiramatsu's avatar Masami Hiramatsu Committed by Ingo Molnar
Browse files

perf probe: Cleanup debuginfo related code



Cleanup debuginfo related code to eliminate fragile code which
pointed by Ingo (Thanks!).
1) Invert logic of NO_DWARF_SUPPORT to DWARF_SUPPORT.
2) For removing assymetric/local variable ifdefs, introduce
  more helper functions.
3) Change options order to reduce the number of ifdefs.

Reported-by: default avatarIngo Molnar <mingo@elte.hu>
Signed-off-by: default avatarMasami Hiramatsu <mhiramat@redhat.com>
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
LKML-Reference: <1269274229-20442-2-git-send-email-acme@infradead.org>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
parent f3a1f0ea
Loading
Loading
Loading
Loading
+1 −2
Original line number Diff line number Diff line
@@ -506,9 +506,8 @@ endif

ifneq ($(shell sh -c "(echo '\#include <dwarf.h>'; echo '\#include <libdw.h>'; echo 'int main(void) { Dwarf *dbg; dbg = dwarf_begin(0, DWARF_C_READ); return (long)dbg; }') | $(CC) -x c - $(ALL_CFLAGS) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 -I/usr/include/elfutils -ldw -lelf -o $(BITBUCKET) $(ALL_LDFLAGS) $(EXTLIBS) "$(QUIET_STDERR)" && echo y"), y)
	msg := $(warning No libdw.h found or old libdw.h found, disables dwarf support. Please install elfutils-devel/elfutils-dev);
	BASIC_CFLAGS += -DNO_DWARF_SUPPORT
else
	BASIC_CFLAGS += -I/usr/include/elfutils
	BASIC_CFLAGS += -I/usr/include/elfutils -DDWARF_SUPPORT
	EXTLIBS += -lelf -ldw
	LIB_OBJS += util/probe-finder.o
endif
+13 −15
Original line number Diff line number Diff line
@@ -109,7 +109,7 @@ static int opt_del_probe_event(const struct option *opt __used,
	return 0;
}

#ifndef NO_DWARF_SUPPORT
#ifdef DWARF_SUPPORT
static int opt_show_lines(const struct option *opt __used,
			  const char *str, int unset __used)
{
@@ -126,7 +126,7 @@ static const char * const probe_usage[] = {
	"perf probe [<options>] --add 'PROBEDEF' [--add 'PROBEDEF' ...]",
	"perf probe [<options>] --del '[GROUP:]EVENT' ...",
	"perf probe --list",
#ifndef NO_DWARF_SUPPORT
#ifdef DWARF_SUPPORT
	"perf probe --line 'LINEDESC'",
#endif
	NULL
@@ -135,20 +135,16 @@ static const char * const probe_usage[] = {
static const struct option options[] = {
	OPT_BOOLEAN('v', "verbose", &verbose,
		    "be more verbose (show parsed arguments, etc)"),
#ifndef NO_DWARF_SUPPORT
	OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
		   "file", "vmlinux pathname"),
#endif
	OPT_BOOLEAN('l', "list", &params.list_events,
		    "list up current probe events"),
	OPT_CALLBACK('d', "del", NULL, "[GROUP:]EVENT", "delete a probe event.",
		opt_del_probe_event),
	OPT_CALLBACK('a', "add", NULL,
#ifdef NO_DWARF_SUPPORT
		"[EVENT=]FUNC[+OFF|%return] [ARG ...]",
#else
#ifdef DWARF_SUPPORT
		"[EVENT=]FUNC[@SRC][+OFF|%return|:RL|;PT]|SRC:AL|SRC;PT"
		" [ARG ...]",
#else
		"[EVENT=]FUNC[+OFF|%return] [ARG ...]",
#endif
		"probe point definition, where\n"
		"\t\tGROUP:\tGroup name (optional)\n"
@@ -156,23 +152,25 @@ static const struct option options[] = {
		"\t\tFUNC:\tFunction name\n"
		"\t\tOFF:\tOffset from function entry (in byte)\n"
		"\t\t%return:\tPut the probe at function return\n"
#ifdef NO_DWARF_SUPPORT
		"\t\tARG:\tProbe argument (only \n"
#else
#ifdef DWARF_SUPPORT
		"\t\tSRC:\tSource code path\n"
		"\t\tRL:\tRelative line number from function entry.\n"
		"\t\tAL:\tAbsolute line number in file.\n"
		"\t\tPT:\tLazy expression of line code.\n"
		"\t\tARG:\tProbe argument (local variable name or\n"
#endif
		"\t\t\tkprobe-tracer argument format.)\n",
#else
		"\t\tARG:\tProbe argument (kprobe-tracer argument format.)\n",
#endif
		opt_add_probe_event),
	OPT_BOOLEAN('f', "force", &params.force_add, "forcibly add events"
		    " with existing name"),
#ifndef NO_DWARF_SUPPORT
#ifdef DWARF_SUPPORT
	OPT_CALLBACK('L', "line", NULL,
		     "FUNC[:RLN[+NUM|:RLN2]]|SRC:ALN[+NUM|:ALN2]",
		     "Show source code lines.", opt_show_lines),
	OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
		   "file", "vmlinux pathname"),
#endif
	OPT__DRY_RUN(&probe_event_dry_run),
	OPT_END()
@@ -211,7 +209,7 @@ int cmd_probe(int argc, const char **argv, const char *prefix __used)
		return 0;
	}

#ifndef NO_DWARF_SUPPORT
#ifdef DWARF_SUPPORT
	if (params.show_lines) {
		if (params.nevents != 0 || params.dellist) {
			pr_warning("  Error: Don't use --line with"
+184 −159
Original line number Diff line number Diff line
@@ -42,6 +42,7 @@
#include "color.h"
#include "symbol.h"
#include "thread.h"
#include "trace-event.h"	/* For __unused */
#include "parse-events.h"	/* For debugfs_path */
#include "probe-event.h"
#include "probe-finder.h"
@@ -70,10 +71,11 @@ static int e_snprintf(char *str, size_t size, const char *format, ...)
	return ret;
}

static char *synthesize_perf_probe_point(struct perf_probe_point *pp);
static struct map_groups kmap_groups;
static struct map *kmaps[MAP__NR_TYPES];

/* Initialize symbol maps for vmlinux */
/* Initialize symbol maps and path of vmlinux */
static void init_vmlinux(void)
{
	symbol_conf.sort_by_name = true;
@@ -89,7 +91,7 @@ static void init_vmlinux(void)
		die("Failed to create kernel maps.");
}

#ifndef NO_DWARF_SUPPORT
#ifdef DWARF_SUPPORT
static int open_vmlinux(void)
{
	if (map__load(kmaps[MAP__FUNCTION], NULL) < 0) {
@@ -99,6 +101,176 @@ static int open_vmlinux(void)
	pr_debug("Try to open %s\n", kmaps[MAP__FUNCTION]->dso->long_name);
	return open(kmaps[MAP__FUNCTION]->dso->long_name, O_RDONLY);
}

static void convert_to_perf_probe_point(struct kprobe_trace_point *tp,
					struct perf_probe_point *pp)
{
	struct symbol *sym;
	int fd, ret = 0;

	sym = map__find_symbol_by_name(kmaps[MAP__FUNCTION],
				       tp->symbol, NULL);
	if (sym) {
		fd = open_vmlinux();
		ret = find_perf_probe_point(fd, sym->start + tp->offset, pp);
		close(fd);
	}
	if (ret <= 0) {
		pp->function = xstrdup(tp->symbol);
		pp->offset = tp->offset;
	}
	pp->retprobe = tp->retprobe;
}

/* Try to find perf_probe_event with debuginfo */
static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev,
					   struct kprobe_trace_event **tevs)
{
	bool need_dwarf = perf_probe_event_need_dwarf(pev);
	int fd, ntevs;

	fd = open_vmlinux();
	if (fd < 0) {
		if (need_dwarf)
			die("Could not open debuginfo file.");

		pr_debug("Could not open vmlinux. Try to use symbols.\n");
		return 0;
	}

	/* Searching trace events corresponding to probe event */
	ntevs = find_kprobe_trace_events(fd, pev, tevs);
	close(fd);

	if (ntevs > 0)	/* Succeeded to find trace events */
		return ntevs;

	if (ntevs == 0)	/* No error but failed to find probe point. */
		die("Probe point '%s' not found. - probe not added.",
		    synthesize_perf_probe_point(&pev->point));

	/* Error path */
	if (need_dwarf) {
		if (ntevs == -ENOENT)
			pr_warning("No dwarf info found in the vmlinux - "
				"please rebuild with CONFIG_DEBUG_INFO=y.\n");
		die("Could not analyze debuginfo.");
	}
	pr_debug("An error occurred in debuginfo analysis."
		 " Try to use symbols.\n");
	return 0;

}

#define LINEBUF_SIZE 256
#define NR_ADDITIONAL_LINES 2

static void show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num)
{
	char buf[LINEBUF_SIZE];
	const char *color = PERF_COLOR_BLUE;

	if (fgets(buf, LINEBUF_SIZE, fp) == NULL)
		goto error;
	if (!skip) {
		if (show_num)
			fprintf(stdout, "%7u  %s", l, buf);
		else
			color_fprintf(stdout, color, "         %s", buf);
	}

	while (strlen(buf) == LINEBUF_SIZE - 1 &&
	       buf[LINEBUF_SIZE - 2] != '\n') {
		if (fgets(buf, LINEBUF_SIZE, fp) == NULL)
			goto error;
		if (!skip) {
			if (show_num)
				fprintf(stdout, "%s", buf);
			else
				color_fprintf(stdout, color, "%s", buf);
		}
	}
	return;
error:
	if (feof(fp))
		die("Source file is shorter than expected.");
	else
		die("File read error: %s", strerror(errno));
}

/*
 * Show line-range always requires debuginfo to find source file and
 * line number.
 */
void show_line_range(struct line_range *lr)
{
	unsigned int l = 1;
	struct line_node *ln;
	FILE *fp;
	int fd, ret;

	/* Search a line range */
	init_vmlinux();
	fd = open_vmlinux();
	if (fd < 0)
		die("Could not open debuginfo file.");
	ret = find_line_range(fd, lr);
	if (ret <= 0)
		die("Source line is not found.\n");
	close(fd);

	setup_pager();

	if (lr->function)
		fprintf(stdout, "<%s:%d>\n", lr->function,
			lr->start - lr->offset);
	else
		fprintf(stdout, "<%s:%d>\n", lr->file, lr->start);

	fp = fopen(lr->path, "r");
	if (fp == NULL)
		die("Failed to open %s: %s", lr->path, strerror(errno));
	/* Skip to starting line number */
	while (l < lr->start)
		show_one_line(fp, l++, true, false);

	list_for_each_entry(ln, &lr->line_list, list) {
		while (ln->line > l)
			show_one_line(fp, (l++) - lr->offset, false, false);
		show_one_line(fp, (l++) - lr->offset, false, true);
	}

	if (lr->end == INT_MAX)
		lr->end = l + NR_ADDITIONAL_LINES;
	while (l < lr->end && !feof(fp))
		show_one_line(fp, (l++) - lr->offset, false, false);

	fclose(fp);
}

#else	/* !DWARF_SUPPORT */

static void convert_to_perf_probe_point(struct kprobe_trace_point *tp,
					struct perf_probe_point *pp)
{
	pp->function = xstrdup(tp->symbol);
	pp->offset = tp->offset;
	pp->retprobe = tp->retprobe;
}

static int try_to_find_kprobe_trace_events(struct perf_probe_event *pev,
				struct kprobe_trace_event **tevs __unused)
{
	if (perf_probe_event_need_dwarf(pev))
		die("Debuginfo-analysis is not supported");
	return 0;
}

void show_line_range(struct line_range *lr __unused)
{
	die("Debuginfo-analysis is not supported");
}

#endif

void parse_line_range_desc(const char *arg, struct line_range *lr)
@@ -592,32 +764,14 @@ void convert_to_perf_probe_event(struct kprobe_trace_event *tev,
{
	char buf[64];
	int i;
#ifndef NO_DWARF_SUPPORT
	struct symbol *sym;
	int fd, ret = 0;

	sym = map__find_symbol_by_name(kmaps[MAP__FUNCTION],
				       tev->point.symbol, NULL);
	if (sym) {
		fd = open_vmlinux();
		ret = find_perf_probe_point(fd, sym->start + tev->point.offset,
					    &pev->point);
		close(fd);
	}
	if (ret <= 0) {
		pev->point.function = xstrdup(tev->point.symbol);
		pev->point.offset = tev->point.offset;
	}
#else
	/* Convert trace_point to probe_point */
	pev->point.function = xstrdup(tev->point.symbol);
	pev->point.offset = tev->point.offset;
#endif
	pev->point.retprobe = tev->point.retprobe;

	/* Convert event/group name */
	pev->event = xstrdup(tev->event);
	pev->group = xstrdup(tev->group);

	/* Convert trace_point to probe_point */
	convert_to_perf_probe_point(&tev->point, &pev->point);

	/* Convert trace_arg to probe_arg */
	pev->nargs = tev->nargs;
	pev->args = xzalloc(sizeof(struct perf_probe_arg) * pev->nargs);
@@ -944,52 +1098,13 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev,
					  struct kprobe_trace_event **tevs)
{
	struct symbol *sym;
	bool need_dwarf;
#ifndef NO_DWARF_SUPPORT
	int fd;
#endif
	int ntevs = 0, i;
	struct kprobe_trace_event *tev;

	need_dwarf = perf_probe_event_need_dwarf(pev);

	if (need_dwarf)
#ifdef NO_DWARF_SUPPORT
		die("Debuginfo-analysis is not supported");
#else	/* !NO_DWARF_SUPPORT */
		pr_debug("Some probes require debuginfo.\n");

	fd = open_vmlinux();
	if (fd < 0) {
		if (need_dwarf)
			die("Could not open debuginfo file.");

		pr_debug("Could not open vmlinux/module file."
			 " Try to use symbols.\n");
		goto end_dwarf;
	}

	/* Searching probe points */
	ntevs = find_kprobe_trace_events(fd, pev, tevs);

	if (ntevs > 0)	/* Found */
		goto found;

	if (ntevs == 0)	/* No error but failed to find probe point. */
		die("Probe point '%s' not found. - probe not added.",
		    synthesize_perf_probe_point(&pev->point));

	/* Error path */
	if (need_dwarf) {
		if (ntevs == -ENOENT)
			pr_warning("No dwarf info found in the vmlinux - please rebuild with CONFIG_DEBUG_INFO=y.\n");
		die("Could not analyze debuginfo.");
	}
	pr_debug("An error occurred in debuginfo analysis."
		 " Try to use symbols.\n");

end_dwarf:
#endif /* !NO_DWARF_SUPPORT */
	/* Convert perf_probe_event with debuginfo */
	ntevs = try_to_find_kprobe_trace_events(pev, tevs);
	if (ntevs > 0)
		return ntevs;

	/* Allocate trace event buffer */
	ntevs = 1;
@@ -1012,10 +1127,7 @@ static int convert_to_kprobe_trace_events(struct perf_probe_event *pev,
	if (!sym)
		die("Kernel symbol \'%s\' not found - probe not added.",
		    tev->point.symbol);
#ifndef NO_DWARF_SUPPORT
found:
	close(fd);
#endif

	return ntevs;
}

@@ -1133,90 +1245,3 @@ void del_perf_probe_events(struct strlist *dellist)
	close(fd);
}
#define LINEBUF_SIZE 256
#define NR_ADDITIONAL_LINES 2

static void show_one_line(FILE *fp, unsigned int l, bool skip, bool show_num)
{
	char buf[LINEBUF_SIZE];
	const char *color = PERF_COLOR_BLUE;

	if (fgets(buf, LINEBUF_SIZE, fp) == NULL)
		goto error;
	if (!skip) {
		if (show_num)
			fprintf(stdout, "%7u  %s", l, buf);
		else
			color_fprintf(stdout, color, "         %s", buf);
	}

	while (strlen(buf) == LINEBUF_SIZE - 1 &&
	       buf[LINEBUF_SIZE - 2] != '\n') {
		if (fgets(buf, LINEBUF_SIZE, fp) == NULL)
			goto error;
		if (!skip) {
			if (show_num)
				fprintf(stdout, "%s", buf);
			else
				color_fprintf(stdout, color, "%s", buf);
		}
	}
	return;
error:
	if (feof(fp))
		die("Source file is shorter than expected.");
	else
		die("File read error: %s", strerror(errno));
}

void show_line_range(struct line_range *lr)
{
	unsigned int l = 1;
	struct line_node *ln;
	FILE *fp;
#ifndef NO_DWARF_SUPPORT
	int fd, ret;
#endif

	/* Search a line range */
	init_vmlinux();
#ifndef NO_DWARF_SUPPORT
	fd = open_vmlinux();
	if (fd < 0)
		die("Could not open debuginfo file.");
	ret = find_line_range(fd, lr);
	if (ret <= 0)
		die("Source line is not found.\n");
	close(fd);
#endif

	setup_pager();

	if (lr->function)
		fprintf(stdout, "<%s:%d>\n", lr->function,
			lr->start - lr->offset);
	else
		fprintf(stdout, "<%s:%d>\n", lr->file, lr->start);

	fp = fopen(lr->path, "r");
	if (fp == NULL)
		die("Failed to open %s: %s", lr->path, strerror(errno));
	/* Skip to starting line number */
	while (l < lr->start)
		show_one_line(fp, l++, true, false);

	list_for_each_entry(ln, &lr->line_list, list) {
		while (ln->line > l)
			show_one_line(fp, (l++) - lr->offset, false, false);
		show_one_line(fp, (l++) - lr->offset, false, true);
	}

	if (lr->end == INT_MAX)
		lr->end = l + NR_ADDITIONAL_LINES;
	while (l < lr->end && !feof(fp))
		show_one_line(fp, (l++) - lr->offset, false, false);

	fclose(fp);
}

+2 −2
Original line number Diff line number Diff line
@@ -15,7 +15,7 @@ static inline int is_c_varname(const char *name)
	return isalpha(name[0]) || name[0] == '_';
}

#ifndef NO_DWARF_SUPPORT
#ifdef DWARF_SUPPORT
/* Find kprobe_trace_events specified by perf_probe_event from debuginfo */
extern int find_kprobe_trace_events(int fd, struct perf_probe_event *pev,
				    struct kprobe_trace_event **tevs);
@@ -57,6 +57,6 @@ struct line_finder {
	int			found;
};

#endif /* NO_DWARF_SUPPORT */
#endif /* DWARF_SUPPORT */

#endif /*_PROBE_FINDER_H */