aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFrederic Weisbecker <fweisbec@gmail.com>2009-08-17 10:18:08 -0400
committerIngo Molnar <mingo@elte.hu>2009-08-17 10:32:39 -0400
commit5f9c39dca52d3e639ac899e169f408c6fd8396cc (patch)
treec4274c18c95e686312744cebad45d1537c606807
parentea4010d1363699770a9894493bafe556a59a144c (diff)
perf tools: Add perf trace
This adds perf trace into the set of perf tools. It is written to fetch the tracepoint samples from perf events and display them, according to the events information given by the debugfs files through the util/trace* tools. It is a rough first shot and doesn't yet handle the cpu, timestamps fields and some other things. Example: perf record -f -e workqueue:workqueue_execution:record -F 1 -a perf trace kblockd/0-236 [000] 0.000000: workqueue_execution: thread=:236 func=cfq_kick_queue+0x0 kondemand/0-360 [000] 0.000000: workqueue_execution: thread=:360 func=do_dbs_timer+0x0 kondemand/0-360 [000] 0.000000: workqueue_execution: thread=:360 func=do_dbs_timer+0x0 kondemand/1-361 [000] 0.000000: workqueue_execution: thread=:361 func=do_dbs_timer+0x0 kondemand/1-361 [000] 0.000000: workqueue_execution: thread=:361 func=do_dbs_timer+0x0 kondemand/1-361 [000] 0.000000: workqueue_execution: thread=:361 func=do_dbs_timer+0x0 kondemand/1-361 [000] 0.000000: workqueue_execution: thread=:361 func=do_dbs_timer+0x0 kondemand/1-361 [000] 0.000000: workqueue_execution: thread=:361 func=do_dbs_timer+0x0 kondemand/1-361 [000] 0.000000: workqueue_execution: thread=:361 func=do_dbs_timer+0x0 kondemand/1-361 [000] 0.000000: workqueue_execution: thread=:361 func=do_dbs_timer+0x0 kondemand/1-361 [000] 0.000000: workqueue_execution: thread=:361 func=do_dbs_timer+0x0 kondemand/1-361 [000] 0.000000: workqueue_execution: thread=:361 func=do_dbs_timer+0x0 kondemand/1-361 [000] 0.000000: workqueue_execution: thread=:361 func=do_dbs_timer+0x0 kondemand/1-361 [000] 0.000000: workqueue_execution: thread=:361 func=do_dbs_timer+0x0 kondemand/1-361 [000] 0.000000: workqueue_execution: thread=:361 func=do_dbs_timer+0x0 kondemand/1-361 [000] 0.000000: workqueue_execution: thread=:361 func=do_dbs_timer+0x0 Todo: - A lot of things! Signed-off-by: Frederic Weisbecker <fweisbec@gmail.com> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Arnaldo Carvalho de Melo <acme@redhat.com> Cc: Mike Galbraith <efault@gmx.de> Cc: "Luis Claudio R. Goncalves" <lclaudio@uudg.org> Cc: Clark Williams <williams@redhat.com> Cc: Jon Masters <jonathan@jonmasters.org> Cc: Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca> Cc: Christoph Hellwig <hch@infradead.org> Cc: Xiao Guangrong <xiaoguangrong@cn.fujitsu.com> Cc: Zhaolei <zhaolei@cn.fujitsu.com> Cc: Li Zefan <lizf@cn.fujitsu.com> Cc: Lai Jiangshan <laijs@cn.fujitsu.com> Cc: Masami Hiramatsu <mhiramat@redhat.com> Cc: Tom Zanussi <tzanussi@gmail.com> Cc: "Frank Ch. Eigler" <fche@redhat.com> Cc: Roland McGrath <roland@redhat.com> Cc: Jason Baron <jbaron@redhat.com> Cc: Paul Mackerras <paulus@samba.org> Cc: Jiaying Zhang <jiayingz@google.com> Cc: Anton Blanchard <anton@samba.org> LKML-Reference: <1250518688-7207-4-git-send-email-fweisbec@gmail.com> Signed-off-by: Ingo Molnar <mingo@elte.hu>
-rw-r--r--tools/perf/Makefile4
-rw-r--r--tools/perf/builtin-record.c4
-rw-r--r--tools/perf/builtin-trace.c277
-rw-r--r--tools/perf/perf.c1
-rw-r--r--tools/perf/util/util.h1
5 files changed, 287 insertions, 0 deletions
diff --git a/tools/perf/Makefile b/tools/perf/Makefile
index 5d54ddb83ab1..c481a513f551 100644
--- a/tools/perf/Makefile
+++ b/tools/perf/Makefile
@@ -347,6 +347,9 @@ LIB_OBJS += util/values.o
347LIB_OBJS += util/debug.o 347LIB_OBJS += util/debug.o
348LIB_OBJS += util/map.o 348LIB_OBJS += util/map.o
349LIB_OBJS += util/thread.o 349LIB_OBJS += util/thread.o
350LIB_OBJS += util/trace-event-parse.o
351LIB_OBJS += util/trace-event-read.o
352LIB_OBJS += util/trace-event-info.o
350 353
351BUILTIN_OBJS += builtin-annotate.o 354BUILTIN_OBJS += builtin-annotate.o
352BUILTIN_OBJS += builtin-help.o 355BUILTIN_OBJS += builtin-help.o
@@ -355,6 +358,7 @@ BUILTIN_OBJS += builtin-record.o
355BUILTIN_OBJS += builtin-report.o 358BUILTIN_OBJS += builtin-report.o
356BUILTIN_OBJS += builtin-stat.o 359BUILTIN_OBJS += builtin-stat.o
357BUILTIN_OBJS += builtin-top.o 360BUILTIN_OBJS += builtin-top.o
361BUILTIN_OBJS += builtin-trace.o
358 362
359PERFLIBS = $(LIB_FILE) 363PERFLIBS = $(LIB_FILE)
360 364
diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c
index 6a5db675ee4f..3ce2f03f217a 100644
--- a/tools/perf/builtin-record.c
+++ b/tools/perf/builtin-record.c
@@ -17,6 +17,7 @@
17#include "util/header.h" 17#include "util/header.h"
18#include "util/event.h" 18#include "util/event.h"
19#include "util/debug.h" 19#include "util/debug.h"
20#include "util/trace-event.h"
20 21
21#include <unistd.h> 22#include <unistd.h>
22#include <sched.h> 23#include <sched.h>
@@ -519,6 +520,9 @@ static int __cmd_record(int argc, const char **argv)
519 signal(SIGCHLD, sig_handler); 520 signal(SIGCHLD, sig_handler);
520 signal(SIGINT, sig_handler); 521 signal(SIGINT, sig_handler);
521 522
523 if (raw_samples)
524 read_tracing_data();
525
522 if (!stat(output_name, &st) && st.st_size) { 526 if (!stat(output_name, &st) && st.st_size) {
523 if (!force && !append_file) { 527 if (!force && !append_file) {
524 fprintf(stderr, "Error, output file %s exists, use -A to append or -f to overwrite.\n", 528 fprintf(stderr, "Error, output file %s exists, use -A to append or -f to overwrite.\n",
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
new file mode 100644
index 000000000000..b160a9f61bf3
--- /dev/null
+++ b/tools/perf/builtin-trace.c
@@ -0,0 +1,277 @@
1#include "builtin.h"
2
3#include "util/util.h"
4#include "util/cache.h"
5#include "util/symbol.h"
6#include "util/thread.h"
7#include "util/header.h"
8
9#include "util/parse-options.h"
10
11#include "perf.h"
12#include "util/debug.h"
13
14#include "util/trace-event.h"
15
16static char const *input_name = "perf.data";
17static int input;
18static unsigned long page_size;
19static unsigned long mmap_window = 32;
20
21static unsigned long total = 0;
22static unsigned long total_comm = 0;
23
24static struct rb_root threads;
25static struct thread *last_match;
26
27static struct perf_header *header;
28static u64 sample_type;
29
30
31static int
32process_comm_event(event_t *event, unsigned long offset, unsigned long head)
33{
34 struct thread *thread;
35
36 thread = threads__findnew(event->comm.pid, &threads, &last_match);
37
38 dump_printf("%p [%p]: PERF_EVENT_COMM: %s:%d\n",
39 (void *)(offset + head),
40 (void *)(long)(event->header.size),
41 event->comm.comm, event->comm.pid);
42
43 if (thread == NULL ||
44 thread__set_comm(thread, event->comm.comm)) {
45 dump_printf("problem processing PERF_EVENT_COMM, skipping event.\n");
46 return -1;
47 }
48 total_comm++;
49
50 return 0;
51}
52
53static int
54process_sample_event(event_t *event, unsigned long offset, unsigned long head)
55{
56 char level;
57 int show = 0;
58 struct dso *dso = NULL;
59 struct thread *thread;
60 u64 ip = event->ip.ip;
61 u64 period = 1;
62 void *more_data = event->ip.__more_data;
63 int cpumode;
64
65 thread = threads__findnew(event->ip.pid, &threads, &last_match);
66
67 if (sample_type & PERF_SAMPLE_PERIOD) {
68 period = *(u64 *)more_data;
69 more_data += sizeof(u64);
70 }
71
72 dump_printf("%p [%p]: PERF_EVENT_SAMPLE (IP, %d): %d/%d: %p period: %Ld\n",
73 (void *)(offset + head),
74 (void *)(long)(event->header.size),
75 event->header.misc,
76 event->ip.pid, event->ip.tid,
77 (void *)(long)ip,
78 (long long)period);
79
80 dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid);
81
82 if (thread == NULL) {
83 eprintf("problem processing %d event, skipping it.\n",
84 event->header.type);
85 return -1;
86 }
87
88 cpumode = event->header.misc & PERF_EVENT_MISC_CPUMODE_MASK;
89
90 if (cpumode == PERF_EVENT_MISC_KERNEL) {
91 show = SHOW_KERNEL;
92 level = 'k';
93
94 dso = kernel_dso;
95
96 dump_printf(" ...... dso: %s\n", dso->name);
97
98 } else if (cpumode == PERF_EVENT_MISC_USER) {
99
100 show = SHOW_USER;
101 level = '.';
102
103 } else {
104 show = SHOW_HV;
105 level = 'H';
106
107 dso = hypervisor_dso;
108
109 dump_printf(" ...... dso: [hypervisor]\n");
110 }
111
112 if (sample_type & PERF_SAMPLE_RAW) {
113 struct {
114 u32 size;
115 char data[0];
116 } *raw = more_data;
117
118 /*
119 * FIXME: better resolve from pid from the struct trace_entry
120 * field, although it should be the same than this perf
121 * event pid
122 */
123 print_event(0, raw->data, raw->size, 0, thread->comm);
124 }
125 total += period;
126
127 return 0;
128}
129
130static int
131process_event(event_t *event, unsigned long offset, unsigned long head)
132{
133 trace_event(event);
134
135 switch (event->header.type) {
136 case PERF_EVENT_MMAP ... PERF_EVENT_LOST:
137 return 0;
138
139 case PERF_EVENT_COMM:
140 return process_comm_event(event, offset, head);
141
142 case PERF_EVENT_EXIT ... PERF_EVENT_READ:
143 return 0;
144
145 case PERF_EVENT_SAMPLE:
146 return process_sample_event(event, offset, head);
147
148 case PERF_EVENT_MAX:
149 default:
150 return -1;
151 }
152
153 return 0;
154}
155
156static int __cmd_trace(void)
157{
158 int ret, rc = EXIT_FAILURE;
159 unsigned long offset = 0;
160 unsigned long head = 0;
161 struct stat perf_stat;
162 event_t *event;
163 uint32_t size;
164 char *buf;
165
166 trace_report();
167
168 input = open(input_name, O_RDONLY);
169 if (input < 0) {
170 perror("failed to open file");
171 exit(-1);
172 }
173
174 ret = fstat(input, &perf_stat);
175 if (ret < 0) {
176 perror("failed to stat file");
177 exit(-1);
178 }
179
180 if (!perf_stat.st_size) {
181 fprintf(stderr, "zero-sized file, nothing to do!\n");
182 exit(0);
183 }
184 header = perf_header__read(input);
185 sample_type = perf_header__sample_type(header);
186
187 if (load_kernel() < 0) {
188 perror("failed to load kernel symbols");
189 return EXIT_FAILURE;
190 }
191
192remap:
193 buf = (char *)mmap(NULL, page_size * mmap_window, PROT_READ,
194 MAP_SHARED, input, offset);
195 if (buf == MAP_FAILED) {
196 perror("failed to mmap file");
197 exit(-1);
198 }
199
200more:
201 event = (event_t *)(buf + head);
202
203 size = event->header.size;
204 if (!size)
205 size = 8;
206
207 if (head + event->header.size >= page_size * mmap_window) {
208 unsigned long shift = page_size * (head / page_size);
209 int res;
210
211 res = munmap(buf, page_size * mmap_window);
212 assert(res == 0);
213
214 offset += shift;
215 head -= shift;
216 goto remap;
217 }
218
219 size = event->header.size;
220
221
222 if (!size || process_event(event, offset, head) < 0) {
223
224 /*
225 * assume we lost track of the stream, check alignment, and
226 * increment a single u64 in the hope to catch on again 'soon'.
227 */
228
229 if (unlikely(head & 7))
230 head &= ~7ULL;
231
232 size = 8;
233 }
234
235 head += size;
236
237 if (offset + head < (unsigned long)perf_stat.st_size)
238 goto more;
239
240 rc = EXIT_SUCCESS;
241 close(input);
242
243 return rc;
244}
245
246static const char * const annotate_usage[] = {
247 "perf trace [<options>] <command>",
248 NULL
249};
250
251static const struct option options[] = {
252 OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace,
253 "dump raw trace in ASCII"),
254 OPT_BOOLEAN('v', "verbose", &verbose,
255 "be more verbose (show symbol address, etc)"),
256};
257
258int cmd_trace(int argc, const char **argv, const char *prefix __used)
259{
260 symbol__init();
261 page_size = getpagesize();
262
263 argc = parse_options(argc, argv, options, annotate_usage, 0);
264 if (argc) {
265 /*
266 * Special case: if there's an argument left then assume tha
267 * it's a symbol filter:
268 */
269 if (argc > 1)
270 usage_with_options(annotate_usage, options);
271 }
272
273
274 setup_pager();
275
276 return __cmd_trace();
277}
diff --git a/tools/perf/perf.c b/tools/perf/perf.c
index 31982ad064b4..fe4589dde950 100644
--- a/tools/perf/perf.c
+++ b/tools/perf/perf.c
@@ -292,6 +292,7 @@ static void handle_internal_command(int argc, const char **argv)
292 { "top", cmd_top, 0 }, 292 { "top", cmd_top, 0 },
293 { "annotate", cmd_annotate, 0 }, 293 { "annotate", cmd_annotate, 0 },
294 { "version", cmd_version, 0 }, 294 { "version", cmd_version, 0 },
295 { "trace", cmd_trace, 0 },
295 }; 296 };
296 unsigned int i; 297 unsigned int i;
297 static const char ext[] = STRIP_EXTENSION; 298 static const char ext[] = STRIP_EXTENSION;
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h
index d61a6f037631..c62ef9720c6a 100644
--- a/tools/perf/util/util.h
+++ b/tools/perf/util/util.h
@@ -311,6 +311,7 @@ static inline int has_extension(const char *filename, const char *ext)
311#undef isspace 311#undef isspace
312#undef isdigit 312#undef isdigit
313#undef isalpha 313#undef isalpha
314#undef isprint
314#undef isalnum 315#undef isalnum
315#undef tolower 316#undef tolower
316#undef toupper 317#undef toupper