Ver Fonte

Merge branch 'perf/core' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux-2.6 into perf/core

Ingo Molnar há 14 anos atrás
pai
commit
67b96c182c

+ 3 - 0
tools/perf/Documentation/perf-report.txt

@@ -104,6 +104,9 @@ OPTIONS
 --vmlinux=<file>::
 --vmlinux=<file>::
         vmlinux pathname
         vmlinux pathname
 
 
+--kallsyms=<file>::
+        kallsyms pathname
+
 -m::
 -m::
 --modules::
 --modules::
         Load module symbols. WARNING: This should only be used with -k and
         Load module symbols. WARNING: This should only be used with -k and

+ 2 - 0
tools/perf/builtin-report.c

@@ -443,6 +443,8 @@ static const struct option options[] = {
 		    "dump raw trace in ASCII"),
 		    "dump raw trace in ASCII"),
 	OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
 	OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name,
 		   "file", "vmlinux pathname"),
 		   "file", "vmlinux pathname"),
+	OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name,
+		   "file", "kallsyms pathname"),
 	OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
 	OPT_BOOLEAN('f', "force", &force, "don't complain, do it"),
 	OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
 	OPT_BOOLEAN('m', "modules", &symbol_conf.use_modules,
 		    "load module symbols - WARNING: use only with -k and LIVE kernel"),
 		    "load module symbols - WARNING: use only with -k and LIVE kernel"),

+ 11 - 1
tools/perf/util/event.c

@@ -7,7 +7,7 @@
 #include "strlist.h"
 #include "strlist.h"
 #include "thread.h"
 #include "thread.h"
 
 
-const char *event__name[] = {
+static const char *event__name[] = {
 	[0]			 = "TOTAL",
 	[0]			 = "TOTAL",
 	[PERF_RECORD_MMAP]	 = "MMAP",
 	[PERF_RECORD_MMAP]	 = "MMAP",
 	[PERF_RECORD_LOST]	 = "LOST",
 	[PERF_RECORD_LOST]	 = "LOST",
@@ -22,8 +22,18 @@ const char *event__name[] = {
 	[PERF_RECORD_HEADER_EVENT_TYPE]	 = "EVENT_TYPE",
 	[PERF_RECORD_HEADER_EVENT_TYPE]	 = "EVENT_TYPE",
 	[PERF_RECORD_HEADER_TRACING_DATA]	 = "TRACING_DATA",
 	[PERF_RECORD_HEADER_TRACING_DATA]	 = "TRACING_DATA",
 	[PERF_RECORD_HEADER_BUILD_ID]	 = "BUILD_ID",
 	[PERF_RECORD_HEADER_BUILD_ID]	 = "BUILD_ID",
+	[PERF_RECORD_FINISHED_ROUND]	 = "FINISHED_ROUND",
 };
 };
 
 
+const char *event__get_event_name(unsigned int id)
+{
+	if (id >= ARRAY_SIZE(event__name))
+		return "INVALID";
+	if (!event__name[id])
+		return "UNKNOWN";
+	return event__name[id];
+}
+
 static struct sample_data synth_sample = {
 static struct sample_data synth_sample = {
 	.pid	   = -1,
 	.pid	   = -1,
 	.tid	   = -1,
 	.tid	   = -1,

+ 2 - 1
tools/perf/util/event.h

@@ -85,6 +85,7 @@ struct build_id_event {
 };
 };
 
 
 enum perf_user_event_type { /* above any possible kernel type */
 enum perf_user_event_type { /* above any possible kernel type */
+	PERF_RECORD_USER_TYPE_START		= 64,
 	PERF_RECORD_HEADER_ATTR			= 64,
 	PERF_RECORD_HEADER_ATTR			= 64,
 	PERF_RECORD_HEADER_EVENT_TYPE		= 65,
 	PERF_RECORD_HEADER_EVENT_TYPE		= 65,
 	PERF_RECORD_HEADER_TRACING_DATA		= 66,
 	PERF_RECORD_HEADER_TRACING_DATA		= 66,
@@ -171,6 +172,6 @@ int event__preprocess_sample(const event_t *self, struct perf_session *session,
 int event__parse_sample(const event_t *event, struct perf_session *session,
 int event__parse_sample(const event_t *event, struct perf_session *session,
 			struct sample_data *sample);
 			struct sample_data *sample);
 
 
-extern const char *event__name[];
+const char *event__get_event_name(unsigned int id);
 
 
 #endif /* __PERF_RECORD_H */
 #endif /* __PERF_RECORD_H */

+ 6 - 3
tools/perf/util/hist.c

@@ -1168,10 +1168,13 @@ size_t hists__fprintf_nr_events(struct hists *self, FILE *fp)
 	size_t ret = 0;
 	size_t ret = 0;
 
 
 	for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) {
 	for (i = 0; i < PERF_RECORD_HEADER_MAX; ++i) {
-		if (!event__name[i])
+		const char *name = event__get_event_name(i);
+
+		if (!strcmp(name, "UNKNOWN"))
 			continue;
 			continue;
-		ret += fprintf(fp, "%10s events: %10d\n",
-			       event__name[i], self->stats.nr_events[i]);
+
+		ret += fprintf(fp, "%16s events: %10d\n", name,
+			       self->stats.nr_events[i]);
 	}
 	}
 
 
 	return ret;
 	return ret;

+ 95 - 59
tools/perf/util/session.c

@@ -444,6 +444,7 @@ static event__swap_op event__swap_ops[] = {
 
 
 struct sample_queue {
 struct sample_queue {
 	u64			timestamp;
 	u64			timestamp;
+	u64			file_offset;
 	event_t			*event;
 	event_t			*event;
 	struct list_head	list;
 	struct list_head	list;
 };
 };
@@ -464,7 +465,8 @@ static void perf_session_free_sample_buffers(struct perf_session *session)
 static int perf_session_deliver_event(struct perf_session *session,
 static int perf_session_deliver_event(struct perf_session *session,
 				      event_t *event,
 				      event_t *event,
 				      struct sample_data *sample,
 				      struct sample_data *sample,
-				      struct perf_event_ops *ops);
+				      struct perf_event_ops *ops,
+				      u64 file_offset);
 
 
 static void flush_sample_queue(struct perf_session *s,
 static void flush_sample_queue(struct perf_session *s,
 			       struct perf_event_ops *ops)
 			       struct perf_event_ops *ops)
@@ -484,7 +486,8 @@ static void flush_sample_queue(struct perf_session *s,
 			break;
 			break;
 
 
 		event__parse_sample(iter->event, s, &sample);
 		event__parse_sample(iter->event, s, &sample);
-		perf_session_deliver_event(s, iter->event, &sample, ops);
+		perf_session_deliver_event(s, iter->event, &sample, ops,
+					   iter->file_offset);
 
 
 		os->last_flush = iter->timestamp;
 		os->last_flush = iter->timestamp;
 		list_del(&iter->list);
 		list_del(&iter->list);
@@ -596,14 +599,14 @@ static void __queue_event(struct sample_queue *new, struct perf_session *s)
 #define MAX_SAMPLE_BUFFER	(64 * 1024 / sizeof(struct sample_queue))
 #define MAX_SAMPLE_BUFFER	(64 * 1024 / sizeof(struct sample_queue))
 
 
 static int perf_session_queue_event(struct perf_session *s, event_t *event,
 static int perf_session_queue_event(struct perf_session *s, event_t *event,
-				    struct sample_data *data)
+				    struct sample_data *data, u64 file_offset)
 {
 {
 	struct ordered_samples *os = &s->ordered_samples;
 	struct ordered_samples *os = &s->ordered_samples;
 	struct list_head *sc = &os->sample_cache;
 	struct list_head *sc = &os->sample_cache;
 	u64 timestamp = data->time;
 	u64 timestamp = data->time;
 	struct sample_queue *new;
 	struct sample_queue *new;
 
 
-	if (!timestamp)
+	if (!timestamp || timestamp == ~0ULL)
 		return -ETIME;
 		return -ETIME;
 
 
 	if (timestamp < s->ordered_samples.last_flush) {
 	if (timestamp < s->ordered_samples.last_flush) {
@@ -628,6 +631,7 @@ static int perf_session_queue_event(struct perf_session *s, event_t *event,
 	}
 	}
 
 
 	new->timestamp = timestamp;
 	new->timestamp = timestamp;
+	new->file_offset = file_offset;
 	new->event = event;
 	new->event = event;
 
 
 	__queue_event(new, s);
 	__queue_event(new, s);
@@ -635,13 +639,10 @@ static int perf_session_queue_event(struct perf_session *s, event_t *event,
 	return 0;
 	return 0;
 }
 }
 
 
-static void callchain__dump(struct sample_data *sample)
+static void callchain__printf(struct sample_data *sample)
 {
 {
 	unsigned int i;
 	unsigned int i;
 
 
-	if (!dump_trace)
-		return;
-
 	printf("... chain: nr:%Lu\n", sample->callchain->nr);
 	printf("... chain: nr:%Lu\n", sample->callchain->nr);
 
 
 	for (i = 0; i < sample->callchain->nr; i++)
 	for (i = 0; i < sample->callchain->nr; i++)
@@ -665,13 +666,48 @@ static void perf_session__print_tstamp(struct perf_session *session,
 		printf("%Lu ", sample->time);
 		printf("%Lu ", sample->time);
 }
 }
 
 
+static void dump_event(struct perf_session *session, event_t *event,
+		       u64 file_offset, struct sample_data *sample)
+{
+	if (!dump_trace)
+		return;
+
+	printf("\n%#Lx [%#x]: event: %d\n", file_offset, event->header.size,
+	       event->header.type);
+
+	trace_event(event);
+
+	if (sample)
+		perf_session__print_tstamp(session, event, sample);
+
+	printf("%#Lx [%#x]: PERF_RECORD_%s", file_offset, event->header.size,
+	       event__get_event_name(event->header.type));
+}
+
+static void dump_sample(struct perf_session *session, event_t *event,
+			struct sample_data *sample)
+{
+	if (!dump_trace)
+		return;
+
+	printf("(IP, %d): %d/%d: %#Lx period: %Ld\n", event->header.misc,
+	       sample->pid, sample->tid, sample->ip, sample->period);
+
+	if (session->sample_type & PERF_SAMPLE_CALLCHAIN)
+		callchain__printf(sample);
+}
+
 static int perf_session_deliver_event(struct perf_session *session,
 static int perf_session_deliver_event(struct perf_session *session,
 				      event_t *event,
 				      event_t *event,
 				      struct sample_data *sample,
 				      struct sample_data *sample,
-				      struct perf_event_ops *ops)
+				      struct perf_event_ops *ops,
+				      u64 file_offset)
 {
 {
+	dump_event(session, event, file_offset, sample);
+
 	switch (event->header.type) {
 	switch (event->header.type) {
 	case PERF_RECORD_SAMPLE:
 	case PERF_RECORD_SAMPLE:
+		dump_sample(session, event, sample);
 		return ops->sample(event, sample, session);
 		return ops->sample(event, sample, session);
 	case PERF_RECORD_MMAP:
 	case PERF_RECORD_MMAP:
 		return ops->mmap(event, sample, session);
 		return ops->mmap(event, sample, session);
@@ -695,54 +731,29 @@ static int perf_session_deliver_event(struct perf_session *session,
 	}
 	}
 }
 }
 
 
-static int perf_session__process_event(struct perf_session *session,
-				       event_t *event,
-				       struct perf_event_ops *ops,
-				       u64 file_offset)
+static int perf_session__preprocess_sample(struct perf_session *session,
+					   event_t *event, struct sample_data *sample)
 {
 {
-	struct sample_data sample;
-	int ret;
-
-	trace_event(event);
-
-	if (session->header.needs_swap && event__swap_ops[event->header.type])
-		event__swap_ops[event->header.type](event);
+	if (event->header.type != PERF_RECORD_SAMPLE ||
+	    !(session->sample_type & PERF_SAMPLE_CALLCHAIN))
+		return 0;
 
 
-	if (event->header.type >= PERF_RECORD_MMAP &&
-	    event->header.type <= PERF_RECORD_SAMPLE) {
-		event__parse_sample(event, session, &sample);
-		if (dump_trace)
-			perf_session__print_tstamp(session, event, &sample);
+	if (!ip_callchain__valid(sample->callchain, event)) {
+		pr_debug("call-chain problem with event, skipping it.\n");
+		++session->hists.stats.nr_invalid_chains;
+		session->hists.stats.total_invalid_chains += sample->period;
+		return -EINVAL;
 	}
 	}
+	return 0;
+}
 
 
-	if (event->header.type < PERF_RECORD_HEADER_MAX) {
-		dump_printf("%#Lx [%#x]: PERF_RECORD_%s",
-			    file_offset, event->header.size,
-			    event__name[event->header.type]);
-		hists__inc_nr_events(&session->hists, event->header.type);
-	}
+static int perf_session__process_user_event(struct perf_session *session, event_t *event,
+					    struct perf_event_ops *ops, u64 file_offset)
+{
+	dump_event(session, event, file_offset, NULL);
 
 
 	/* These events are processed right away */
 	/* These events are processed right away */
 	switch (event->header.type) {
 	switch (event->header.type) {
-	case PERF_RECORD_SAMPLE:
-		dump_printf("(IP, %d): %d/%d: %#Lx period: %Ld\n",
-			    event->header.misc,
-			    sample.pid, sample.tid, sample.ip, sample.period);
-
-		if (session->sample_type & PERF_SAMPLE_CALLCHAIN) {
-			if (!ip_callchain__valid(sample.callchain, event)) {
-				pr_debug("call-chain problem with event, "
-					 "skipping it.\n");
-				++session->hists.stats.nr_invalid_chains;
-				session->hists.stats.total_invalid_chains +=
-					sample.period;
-				return 0;
-			}
-
-			callchain__dump(&sample);
-		}
-		break;
-
 	case PERF_RECORD_HEADER_ATTR:
 	case PERF_RECORD_HEADER_ATTR:
 		return ops->attr(event, session);
 		return ops->attr(event, session);
 	case PERF_RECORD_HEADER_EVENT_TYPE:
 	case PERF_RECORD_HEADER_EVENT_TYPE:
@@ -756,16 +767,47 @@ static int perf_session__process_event(struct perf_session *session,
 	case PERF_RECORD_FINISHED_ROUND:
 	case PERF_RECORD_FINISHED_ROUND:
 		return ops->finished_round(event, session, ops);
 		return ops->finished_round(event, session, ops);
 	default:
 	default:
-		break;
+		return -EINVAL;
 	}
 	}
+}
+
+static int perf_session__process_event(struct perf_session *session,
+				       event_t *event,
+				       struct perf_event_ops *ops,
+				       u64 file_offset)
+{
+	struct sample_data sample;
+	int ret;
+
+	if (session->header.needs_swap && event__swap_ops[event->header.type])
+		event__swap_ops[event->header.type](event);
+
+	if (event->header.type >= PERF_RECORD_HEADER_MAX)
+		return -EINVAL;
+
+	hists__inc_nr_events(&session->hists, event->header.type);
+
+	if (event->header.type >= PERF_RECORD_USER_TYPE_START)
+		return perf_session__process_user_event(session, event, ops, file_offset);
+
+	/*
+	 * For all kernel events we get the sample data
+	 */
+	event__parse_sample(event, session, &sample);
+
+	/* Preprocess sample records - precheck callchains */
+	if (perf_session__preprocess_sample(session, event, &sample))
+		return 0;
 
 
 	if (ops->ordered_samples) {
 	if (ops->ordered_samples) {
-		ret = perf_session_queue_event(session, event, &sample);
+		ret = perf_session_queue_event(session, event, &sample,
+					       file_offset);
 		if (ret != -ETIME)
 		if (ret != -ETIME)
 			return ret;
 			return ret;
 	}
 	}
 
 
-	return perf_session_deliver_event(session, event, &sample, ops);
+	return perf_session_deliver_event(session, event, &sample, ops,
+					  file_offset);
 }
 }
 
 
 void perf_event_header__bswap(struct perf_event_header *self)
 void perf_event_header__bswap(struct perf_event_header *self)
@@ -870,9 +912,6 @@ more:
 
 
 	head += size;
 	head += size;
 
 
-	dump_printf("\n%#Lx [%#x]: event: %d\n",
-		    head, event.header.size, event.header.type);
-
 	if (skip > 0)
 	if (skip > 0)
 		head += skip;
 		head += skip;
 
 
@@ -961,9 +1000,6 @@ more:
 
 
 	size = event->header.size;
 	size = event->header.size;
 
 
-	dump_printf("\n%#Lx [%#x]: event: %d\n",
-		    file_pos, event->header.size, event->header.type);
-
 	if (size == 0 ||
 	if (size == 0 ||
 	    perf_session__process_event(session, event, ops, file_pos) < 0) {
 	    perf_session__process_event(session, event, ops, file_pos) < 0) {
 		dump_printf("%#Lx [%#x]: skipping unknown header type: %d\n",
 		dump_printf("%#Lx [%#x]: skipping unknown header type: %d\n",

+ 7 - 2
tools/perf/util/symbol.c

@@ -1830,8 +1830,8 @@ static int dso__load_kernel_sym(struct dso *self, struct map *map,
 	const char *kallsyms_filename = NULL;
 	const char *kallsyms_filename = NULL;
 	char *kallsyms_allocated_filename = NULL;
 	char *kallsyms_allocated_filename = NULL;
 	/*
 	/*
-	 * Step 1: if the user specified a vmlinux filename, use it and only
-	 * it, reporting errors to the user if it cannot be used.
+	 * Step 1: if the user specified a kallsyms or vmlinux filename, use
+	 * it and only it, reporting errors to the user if it cannot be used.
 	 *
 	 *
 	 * For instance, try to analyse an ARM perf.data file _without_ a
 	 * For instance, try to analyse an ARM perf.data file _without_ a
 	 * build-id, or if the user specifies the wrong path to the right
 	 * build-id, or if the user specifies the wrong path to the right
@@ -1844,6 +1844,11 @@ static int dso__load_kernel_sym(struct dso *self, struct map *map,
 	 * validation in dso__load_vmlinux and will bail out if they don't
 	 * validation in dso__load_vmlinux and will bail out if they don't
 	 * match.
 	 * match.
 	 */
 	 */
+	if (symbol_conf.kallsyms_name != NULL) {
+		kallsyms_filename = symbol_conf.kallsyms_name;
+		goto do_kallsyms;
+	}
+
 	if (symbol_conf.vmlinux_name != NULL) {
 	if (symbol_conf.vmlinux_name != NULL) {
 		err = dso__load_vmlinux(self, map,
 		err = dso__load_vmlinux(self, map,
 					symbol_conf.vmlinux_name, filter);
 					symbol_conf.vmlinux_name, filter);

+ 1 - 0
tools/perf/util/symbol.h

@@ -72,6 +72,7 @@ struct symbol_conf {
 			show_cpu_utilization,
 			show_cpu_utilization,
 			initialized;
 			initialized;
 	const char	*vmlinux_name,
 	const char	*vmlinux_name,
+			*kallsyms_name,
 			*source_prefix,
 			*source_prefix,
 			*field_sep;
 			*field_sep;
 	const char	*default_guest_vmlinux_name,
 	const char	*default_guest_vmlinux_name,