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

Commit 075de90c authored by Ingo Molnar's avatar Ingo Molnar
Browse files

Merge branch 'perf/core' of...

Merge branch 'perf/core' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux-2.6 into perf/core
parents c7f9a6f3 36532461
Loading
Loading
Loading
Loading
+2 −0
Original line number Original line Diff line number Diff line
@@ -401,6 +401,7 @@ LIB_H += util/include/dwarf-regs.h
LIB_H += util/include/asm/dwarf2.h
LIB_H += util/include/asm/dwarf2.h
LIB_H += util/include/asm/cpufeature.h
LIB_H += util/include/asm/cpufeature.h
LIB_H += perf.h
LIB_H += perf.h
LIB_H += util/annotate.h
LIB_H += util/cache.h
LIB_H += util/cache.h
LIB_H += util/callchain.h
LIB_H += util/callchain.h
LIB_H += util/build-id.h
LIB_H += util/build-id.h
@@ -444,6 +445,7 @@ LIB_H += $(ARCH_INCLUDE)


LIB_OBJS += $(OUTPUT)util/abspath.o
LIB_OBJS += $(OUTPUT)util/abspath.o
LIB_OBJS += $(OUTPUT)util/alias.o
LIB_OBJS += $(OUTPUT)util/alias.o
LIB_OBJS += $(OUTPUT)util/annotate.o
LIB_OBJS += $(OUTPUT)util/build-id.o
LIB_OBJS += $(OUTPUT)util/build-id.o
LIB_OBJS += $(OUTPUT)util/config.o
LIB_OBJS += $(OUTPUT)util/config.o
LIB_OBJS += $(OUTPUT)util/ctype.o
LIB_OBJS += $(OUTPUT)util/ctype.o
+28 −248
Original line number Original line Diff line number Diff line
@@ -9,6 +9,7 @@


#include "util/util.h"
#include "util/util.h"


#include "util/util.h"
#include "util/color.h"
#include "util/color.h"
#include <linux/list.h>
#include <linux/list.h>
#include "util/cache.h"
#include "util/cache.h"
@@ -18,6 +19,7 @@
#include "perf.h"
#include "perf.h"
#include "util/debug.h"
#include "util/debug.h"


#include "util/annotate.h"
#include "util/event.h"
#include "util/event.h"
#include "util/parse-options.h"
#include "util/parse-options.h"
#include "util/parse-events.h"
#include "util/parse-events.h"
@@ -55,7 +57,18 @@ static int hists__add_entry(struct hists *self, struct addr_location *al)
	if (he == NULL)
	if (he == NULL)
		return -ENOMEM;
		return -ENOMEM;


	return hist_entry__inc_addr_samples(he, al->addr);
	if (he->ms.sym != NULL) {
		/*
		 * All aggregated on the first sym_hist.
		 */
		struct annotation *notes = symbol__annotation(he->ms.sym);
		if (notes->histograms == NULL && symbol__alloc_hist(he->ms.sym, 1) < 0)
			return -ENOMEM;

		return hist_entry__inc_addr_samples(he, 0, al->addr);
	}

	return 0;
}
}


static int process_sample_event(union perf_event *event,
static int process_sample_event(union perf_event *event,
@@ -79,245 +92,10 @@ static int process_sample_event(union perf_event *event,
	return 0;
	return 0;
}
}


static int objdump_line__print(struct objdump_line *self,
static int hist_entry__tty_annotate(struct hist_entry *he, int evidx)
			       struct list_head *head,
			       struct hist_entry *he, u64 len)
{
{
	struct symbol *sym = he->ms.sym;
	return symbol__tty_annotate(he->ms.sym, he->ms.map, evidx,
	static const char *prev_line;
				    print_line, full_paths, 0, 0);
	static const char *prev_color;

	if (self->offset != -1) {
		const char *path = NULL;
		unsigned int hits = 0;
		double percent = 0.0;
		const char *color;
		struct sym_priv *priv = symbol__priv(sym);
		struct sym_ext *sym_ext = priv->ext;
		struct sym_hist *h = priv->hist;
		s64 offset = self->offset;
		struct objdump_line *next = objdump__get_next_ip_line(head, self);

		while (offset < (s64)len &&
		       (next == NULL || offset < next->offset)) {
			if (sym_ext) {
				if (path == NULL)
					path = sym_ext[offset].path;
				percent += sym_ext[offset].percent;
			} else
				hits += h->ip[offset];

			++offset;
		}

		if (sym_ext == NULL && h->sum)
			percent = 100.0 * hits / h->sum;

		color = get_percent_color(percent);

		/*
		 * Also color the filename and line if needed, with
		 * the same color than the percentage. Don't print it
		 * twice for close colored ip with the same filename:line
		 */
		if (path) {
			if (!prev_line || strcmp(prev_line, path)
				       || color != prev_color) {
				color_fprintf(stdout, color, " %s", path);
				prev_line = path;
				prev_color = color;
			}
		}

		color_fprintf(stdout, color, " %7.2f", percent);
		printf(" :	");
		color_fprintf(stdout, PERF_COLOR_BLUE, "%s\n", self->line);
	} else {
		if (!*self->line)
			printf("         :\n");
		else
			printf("         :	%s\n", self->line);
	}

	return 0;
}

static struct rb_root root_sym_ext;

static void insert_source_line(struct sym_ext *sym_ext)
{
	struct sym_ext *iter;
	struct rb_node **p = &root_sym_ext.rb_node;
	struct rb_node *parent = NULL;

	while (*p != NULL) {
		parent = *p;
		iter = rb_entry(parent, struct sym_ext, node);

		if (sym_ext->percent > iter->percent)
			p = &(*p)->rb_left;
		else
			p = &(*p)->rb_right;
	}

	rb_link_node(&sym_ext->node, parent, p);
	rb_insert_color(&sym_ext->node, &root_sym_ext);
}

static void free_source_line(struct hist_entry *he, int len)
{
	struct sym_priv *priv = symbol__priv(he->ms.sym);
	struct sym_ext *sym_ext = priv->ext;
	int i;

	if (!sym_ext)
		return;

	for (i = 0; i < len; i++)
		free(sym_ext[i].path);
	free(sym_ext);

	priv->ext = NULL;
	root_sym_ext = RB_ROOT;
}

/* Get the filename:line for the colored entries */
static void
get_source_line(struct hist_entry *he, int len, const char *filename)
{
	struct symbol *sym = he->ms.sym;
	u64 start;
	int i;
	char cmd[PATH_MAX * 2];
	struct sym_ext *sym_ext;
	struct sym_priv *priv = symbol__priv(sym);
	struct sym_hist *h = priv->hist;

	if (!h->sum)
		return;

	sym_ext = priv->ext = calloc(len, sizeof(struct sym_ext));
	if (!priv->ext)
		return;

	start = he->ms.map->unmap_ip(he->ms.map, sym->start);

	for (i = 0; i < len; i++) {
		char *path = NULL;
		size_t line_len;
		u64 offset;
		FILE *fp;

		sym_ext[i].percent = 100.0 * h->ip[i] / h->sum;
		if (sym_ext[i].percent <= 0.5)
			continue;

		offset = start + i;
		sprintf(cmd, "addr2line -e %s %016" PRIx64, filename, offset);
		fp = popen(cmd, "r");
		if (!fp)
			continue;

		if (getline(&path, &line_len, fp) < 0 || !line_len)
			goto next;

		sym_ext[i].path = malloc(sizeof(char) * line_len + 1);
		if (!sym_ext[i].path)
			goto next;

		strcpy(sym_ext[i].path, path);
		insert_source_line(&sym_ext[i]);

	next:
		pclose(fp);
	}
}

static void print_summary(const char *filename)
{
	struct sym_ext *sym_ext;
	struct rb_node *node;

	printf("\nSorted summary for file %s\n", filename);
	printf("----------------------------------------------\n\n");

	if (RB_EMPTY_ROOT(&root_sym_ext)) {
		printf(" Nothing higher than %1.1f%%\n", MIN_GREEN);
		return;
	}

	node = rb_first(&root_sym_ext);
	while (node) {
		double percent;
		const char *color;
		char *path;

		sym_ext = rb_entry(node, struct sym_ext, node);
		percent = sym_ext->percent;
		color = get_percent_color(percent);
		path = sym_ext->path;

		color_fprintf(stdout, color, " %7.2f %s", percent, path);
		node = rb_next(node);
	}
}

static void hist_entry__print_hits(struct hist_entry *self)
{
	struct symbol *sym = self->ms.sym;
	struct sym_priv *priv = symbol__priv(sym);
	struct sym_hist *h = priv->hist;
	u64 len = sym->end - sym->start, offset;

	for (offset = 0; offset < len; ++offset)
		if (h->ip[offset] != 0)
			printf("%*" PRIx64 ": %" PRIu64 "\n", BITS_PER_LONG / 2,
			       sym->start + offset, h->ip[offset]);
	printf("%*s: %" PRIu64 "\n", BITS_PER_LONG / 2, "h->sum", h->sum);
}

static int hist_entry__tty_annotate(struct hist_entry *he)
{
	struct map *map = he->ms.map;
	struct dso *dso = map->dso;
	struct symbol *sym = he->ms.sym;
	const char *filename = dso->long_name, *d_filename;
	u64 len;
	LIST_HEAD(head);
	struct objdump_line *pos, *n;

	if (hist_entry__annotate(he, &head, 0) < 0)
		return -1;

	if (full_paths)
		d_filename = filename;
	else
		d_filename = basename(filename);

	len = sym->end - sym->start;

	if (print_line) {
		get_source_line(he, len, filename);
		print_summary(filename);
	}

	printf("\n\n------------------------------------------------\n");
	printf(" Percent |	Source code & Disassembly of %s\n", d_filename);
	printf("------------------------------------------------\n");

	if (verbose)
		hist_entry__print_hits(he);

	list_for_each_entry_safe(pos, n, &head, node) {
		objdump_line__print(pos, &head, he, len);
		list_del(&pos->node);
		objdump_line__free(pos);
	}

	if (print_line)
		free_source_line(he, len);

	return 0;
}
}


static void hists__find_annotations(struct hists *self)
static void hists__find_annotations(struct hists *self)
@@ -327,13 +105,13 @@ static void hists__find_annotations(struct hists *self)


	while (nd) {
	while (nd) {
		struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
		struct hist_entry *he = rb_entry(nd, struct hist_entry, rb_node);
		struct sym_priv *priv;
		struct annotation *notes;


		if (he->ms.sym == NULL || he->ms.map->dso->annotate_warned)
		if (he->ms.sym == NULL || he->ms.map->dso->annotate_warned)
			goto find_next;
			goto find_next;


		priv = symbol__priv(he->ms.sym);
		notes = symbol__annotation(he->ms.sym);
		if (priv->hist == NULL) {
		if (notes->histograms == NULL) {
find_next:
find_next:
			if (key == KEY_LEFT)
			if (key == KEY_LEFT)
				nd = rb_prev(nd);
				nd = rb_prev(nd);
@@ -343,7 +121,8 @@ static void hists__find_annotations(struct hists *self)
		}
		}


		if (use_browser > 0) {
		if (use_browser > 0) {
			key = hist_entry__tui_annotate(he);
			/* For now all is aggregated on the first */
			key = hist_entry__tui_annotate(he, 0);
			switch (key) {
			switch (key) {
			case KEY_RIGHT:
			case KEY_RIGHT:
				next = rb_next(nd);
				next = rb_next(nd);
@@ -358,15 +137,16 @@ static void hists__find_annotations(struct hists *self)
			if (next != NULL)
			if (next != NULL)
				nd = next;
				nd = next;
		} else {
		} else {
			hist_entry__tty_annotate(he);
			/* For now all is aggregated on the first */
			hist_entry__tty_annotate(he, 0);
			nd = rb_next(nd);
			nd = rb_next(nd);
			/*
			/*
			 * Since we have a hist_entry per IP for the same
			 * Since we have a hist_entry per IP for the same
			 * symbol, free he->ms.sym->hist to signal we already
			 * symbol, free he->ms.sym->histogram to signal we already
			 * processed this symbol.
			 * processed this symbol.
			 */
			 */
			free(priv->hist);
			free(notes->histograms);
			priv->hist = NULL;
			notes->histograms = NULL;
		}
		}
	}
	}
}
}
@@ -454,7 +234,7 @@ int cmd_annotate(int argc, const char **argv, const char *prefix __used)


	setup_browser(true);
	setup_browser(true);


	symbol_conf.priv_size = sizeof(struct sym_priv);
	symbol_conf.priv_size = sizeof(struct annotation);
	symbol_conf.try_vmlinux_path = true;
	symbol_conf.try_vmlinux_path = true;


	if (symbol__init() < 0)
	if (symbol__init() < 0)
+14 −4
Original line number Original line Diff line number Diff line
@@ -9,6 +9,7 @@


#include "util/util.h"
#include "util/util.h"


#include "util/annotate.h"
#include "util/color.h"
#include "util/color.h"
#include <linux/list.h>
#include <linux/list.h>
#include "util/cache.h"
#include "util/cache.h"
@@ -117,8 +118,17 @@ static int perf_session__add_hist_entry(struct perf_session *session,
	 * so we don't allocated the extra space needed because the stdio
	 * so we don't allocated the extra space needed because the stdio
	 * code will not use it.
	 * code will not use it.
	 */
	 */
	if (use_browser > 0)
	if (al->sym != NULL && use_browser > 0) {
		err = hist_entry__inc_addr_samples(he, al->addr);
		/*
		 * All aggregated on the first sym_hist.
		 */
		struct annotation *notes = symbol__annotation(he->ms.sym);
		if (notes->histograms == NULL &&
		    symbol__alloc_hist(he->ms.sym, 1) < 0)
			err = -ENOMEM;
		else
			err = hist_entry__inc_addr_samples(he, 0, al->addr);
	}


	return err;
	return err;
}
}
@@ -348,7 +358,7 @@ static int __cmd_report(void)
	}
	}


	if (use_browser > 0)
	if (use_browser > 0)
		hists__tui_browse_tree(&session->hists_tree, help);
		hists__tui_browse_tree(&session->hists_tree, help, 0);
	else
	else
		hists__tty_browse_tree(&session->hists_tree, help);
		hists__tty_browse_tree(&session->hists_tree, help);


@@ -508,7 +518,7 @@ int cmd_report(int argc, const char **argv, const char *prefix __used)
	 * implementation.
	 * implementation.
	 */
	 */
	if (use_browser > 0) {
	if (use_browser > 0) {
		symbol_conf.priv_size = sizeof(struct sym_priv);
		symbol_conf.priv_size = sizeof(struct annotation);
		/*
		/*
 		 * For searching by name on the "Browse map details".
 		 * For searching by name on the "Browse map details".
 		 * providing it only in verbose mode not to bloat too
 		 * providing it only in verbose mode not to bloat too
+31 −153
Original line number Original line Diff line number Diff line
@@ -20,6 +20,7 @@


#include "perf.h"
#include "perf.h"


#include "util/annotate.h"
#include "util/cache.h"
#include "util/cache.h"
#include "util/color.h"
#include "util/color.h"
#include "util/evlist.h"
#include "util/evlist.h"
@@ -140,10 +141,7 @@ static int parse_source(struct sym_entry *syme)
	struct symbol *sym;
	struct symbol *sym;
	struct sym_entry_source *source;
	struct sym_entry_source *source;
	struct map *map;
	struct map *map;
	FILE *file;
	int err = -1;
	char command[PATH_MAX*2];
	const char *path;
	u64 len;


	if (!syme)
	if (!syme)
		return -1;
		return -1;
@@ -162,197 +160,80 @@ static int parse_source(struct sym_entry *syme)
		if (syme->src == NULL)
		if (syme->src == NULL)
			return -1;
			return -1;
		pthread_mutex_init(&syme->src->lock, NULL);
		pthread_mutex_init(&syme->src->lock, NULL);
		INIT_LIST_HEAD(&syme->src->head);
	}
	}


	source = syme->src;
	source = syme->src;


	if (source->lines) {
	if (symbol__annotation(sym)->histograms != NULL) {
		pthread_mutex_lock(&source->lock);
		pthread_mutex_lock(&source->lock);
		goto out_assign;
		goto out_assign;
	}
	}
	path = map->dso->long_name;

	len = sym->end - sym->start;

	sprintf(command,
		"objdump --start-address=%#0*" PRIx64 " --stop-address=%#0*" PRIx64 " -dS %s",
		BITS_PER_LONG / 4, map__rip_2objdump(map, sym->start),
		BITS_PER_LONG / 4, map__rip_2objdump(map, sym->end), path);

	file = popen(command, "r");
	if (!file)
		return -1;


	pthread_mutex_lock(&source->lock);
	pthread_mutex_lock(&source->lock);
	source->lines_tail = &source->lines;
	while (!feof(file)) {
		struct source_line *src;
		size_t dummy = 0;
		char *c, *sep;

		src = malloc(sizeof(struct source_line));
		assert(src != NULL);
		memset(src, 0, sizeof(struct source_line));


		if (getline(&src->line, &dummy, file) < 0)
	if (symbol__alloc_hist(sym, top.evlist->nr_entries) < 0) {
			break;
		pr_err("Not enough memory for annotating '%s' symbol!\n",
		if (!src->line)
		       sym->name);
			break;
		goto out_unlock;

		c = strchr(src->line, '\n');
		if (c)
			*c = 0;

		src->next = NULL;
		*source->lines_tail = src;
		source->lines_tail = &src->next;

		src->eip = strtoull(src->line, &sep, 16);
		if (*sep == ':')
			src->eip = map__objdump_2ip(map, src->eip);
		else /* this line has no ip info (e.g. source line) */
			src->eip = 0;
	}
	}
	pclose(file);

	err = symbol__annotate(sym, syme->map, &source->head, 0);
	if (err == 0) {
out_assign:
out_assign:
	sym_filter_entry = syme;
	sym_filter_entry = syme;
	}
out_unlock:
	pthread_mutex_unlock(&source->lock);
	pthread_mutex_unlock(&source->lock);
	return 0;
	return err;
}
}


static void __zero_source_counters(struct sym_entry *syme)
static void __zero_source_counters(struct sym_entry *syme)
{
{
	int i;
	struct symbol *sym = sym_entry__symbol(syme);
	struct source_line *line;
	symbol__annotate_zero_histograms(sym);

	line = syme->src->lines;
	while (line) {
		for (i = 0; i < top.evlist->nr_entries; i++)
			line->count[i] = 0;
		line = line->next;
	}
}
}


static void record_precise_ip(struct sym_entry *syme, int counter, u64 ip)
static void record_precise_ip(struct sym_entry *syme, int counter, u64 ip)
{
{
	struct source_line *line;

	if (syme != sym_filter_entry)
	if (syme != sym_filter_entry)
		return;
		return;


	if (pthread_mutex_trylock(&syme->src->lock))
	if (pthread_mutex_trylock(&syme->src->lock))
		return;
		return;


	if (syme->src == NULL || syme->src->source == NULL)
	ip = syme->map->map_ip(syme->map, ip);
		goto out_unlock;
	symbol__inc_addr_samples(sym_entry__symbol(syme), syme->map, counter, ip);


	for (line = syme->src->lines; line; line = line->next) {
		/* skip lines without IP info */
		if (line->eip == 0)
			continue;
		if (line->eip == ip) {
			line->count[counter]++;
			break;
		}
		if (line->eip > ip)
			break;
	}
out_unlock:
	pthread_mutex_unlock(&syme->src->lock);
	pthread_mutex_unlock(&syme->src->lock);
}
}


#define PATTERN_LEN		(BITS_PER_LONG / 4 + 2)

static void lookup_sym_source(struct sym_entry *syme)
{
	struct symbol *symbol = sym_entry__symbol(syme);
	struct source_line *line;
	char pattern[PATTERN_LEN + 1];

	sprintf(pattern, "%0*" PRIx64 " <", BITS_PER_LONG / 4,
		map__rip_2objdump(syme->map, symbol->start));

	pthread_mutex_lock(&syme->src->lock);
	for (line = syme->src->lines; line; line = line->next) {
		if (memcmp(line->line, pattern, PATTERN_LEN) == 0) {
			syme->src->source = line;
			break;
		}
	}
	pthread_mutex_unlock(&syme->src->lock);
}

static void show_lines(struct source_line *queue, int count, int total)
{
	int i;
	struct source_line *line;

	line = queue;
	for (i = 0; i < count; i++) {
		float pcnt = 100.0*(float)line->count[top.sym_counter]/(float)total;

		printf("%8li %4.1f%%\t%s\n", line->count[top.sym_counter], pcnt, line->line);
		line = line->next;
	}
}

#define TRACE_COUNT     3

static void show_details(struct sym_entry *syme)
static void show_details(struct sym_entry *syme)
{
{
	struct symbol *symbol;
	struct symbol *symbol;
	struct source_line *line;
	int more;
	struct source_line *line_queue = NULL;
	int displayed = 0;
	int line_queue_count = 0, total = 0, more = 0;


	if (!syme)
	if (!syme)
		return;
		return;


	if (!syme->src->source)
	symbol = sym_entry__symbol(syme);
		lookup_sym_source(syme);
	if (!syme->src || symbol__annotation(symbol)->histograms == NULL)

	if (!syme->src->source)
		return;
		return;


	symbol = sym_entry__symbol(syme);
	printf("Showing %s for %s\n", event_name(top.sym_evsel), symbol->name);
	printf("Showing %s for %s\n", event_name(top.sym_evsel), symbol->name);
	printf("  Events  Pcnt (>=%d%%)\n", sym_pcnt_filter);
	printf("  Events  Pcnt (>=%d%%)\n", sym_pcnt_filter);


	pthread_mutex_lock(&syme->src->lock);
	pthread_mutex_lock(&syme->src->lock);
	line = syme->src->source;
	more = symbol__annotate_printf(symbol, syme->map, &syme->src->head,
	while (line) {
				       top.sym_evsel->idx, 0, sym_pcnt_filter,
		total += line->count[top.sym_counter];
				       top.print_entries);
		line = line->next;
	if (top.zero)
	}
		symbol__annotate_zero_histogram(symbol, top.sym_evsel->idx);

	else
	line = syme->src->source;
		symbol__annotate_decay_histogram(symbol, &syme->src->head,
	while (line) {
						 top.sym_evsel->idx);
		float pcnt = 0.0;

		if (!line_queue_count)
			line_queue = line;
		line_queue_count++;

		if (line->count[top.sym_counter])
			pcnt = 100.0 * line->count[top.sym_counter] / (float)total;
		if (pcnt >= (float)sym_pcnt_filter) {
			if (displayed <= top.print_entries)
				show_lines(line_queue, line_queue_count, total);
			else more++;
			displayed += line_queue_count;
			line_queue_count = 0;
			line_queue = NULL;
		} else if (line_queue_count > TRACE_COUNT) {
			line_queue = line_queue->next;
			line_queue_count--;
		}

		line->count[top.sym_counter] = top.zero ? 0 : line->count[top.sym_counter] * 7 / 8;
		line = line->next;
	}
	pthread_mutex_unlock(&syme->src->lock);
	pthread_mutex_unlock(&syme->src->lock);
	if (more)
	if (more != 0)
		printf("%d lines not displayed, maybe increase display entries [e]\n", more);
		printf("%d lines not displayed, maybe increase display entries [e]\n", more);
}
}


@@ -787,9 +668,6 @@ static int symbol_filter(struct map *map, struct symbol *sym)
		}
		}
	}
	}


	if (!syme->skip)
		syme->name_len = strlen(sym->name);

	return 0;
	return 0;
}
}


@@ -1175,7 +1053,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)


	top.sym_evsel = list_entry(top.evlist->entries.next, struct perf_evsel, node);
	top.sym_evsel = list_entry(top.evlist->entries.next, struct perf_evsel, node);


	symbol_conf.priv_size = (sizeof(struct sym_entry) +
	symbol_conf.priv_size = (sizeof(struct sym_entry) + sizeof(struct annotation) +
				 (top.evlist->nr_entries + 1) * sizeof(unsigned long));
				 (top.evlist->nr_entries + 1) * sizeof(unsigned long));


	symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL);
	symbol_conf.try_vmlinux_path = (symbol_conf.vmlinux_name == NULL);
+550 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading