aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf/builtin-trace.c
diff options
context:
space:
mode:
authorArnaldo Carvalho de Melo <acme@redhat.com>2012-09-26 19:05:56 -0400
committerArnaldo Carvalho de Melo <acme@redhat.com>2012-09-26 19:42:23 -0400
commit514f1c67c2fdae7b334fdc5adee63a484781241a (patch)
tree4aa41fed839dea9806d8c905c7baec6422b6196b /tools/perf/builtin-trace.c
parent201b7334dc4e977479eb22b106ee8ec506e5e55c (diff)
perf trace: New tool
Initially should look loosely like the venerable 'strace' tool, but using the infrastructure in the perf tools to allow tracing extra targets: [acme@sandy linux]$ perf trace --hell Error: unknown option `hell' usage: perf trace <PID> -p, --pid <pid> trace events on existing process id --tid <tid> trace events on existing thread id --all-cpus system-wide collection from all CPUs --cpu <cpu> list of cpus to monitor --no-inherit child tasks do not inherit counters --mmap-pages <n> number of mmap data pages --uid <user> user to profile [acme@sandy linux]$ Those should have the same semantics as when using with 'perf record'. It gets stuck sometimes, but hey, it works sometimes too! In time it should support perf.data based workloads, i.e. it should have a: -o filename Command line option that will produce a perf.data file that can then be used with 'perf trace' or any of the other perf tools (script, report, etc). It will also eventually have the set of functionalities described in the previous 'trace' prototype by Thomas Gleixner: "Announcing a new utility: 'trace'" http://lwn.net/Articles/415728/ Also planned is to have some of the features suggested in the comments of that LWN article. Cc: David Ahern <dsahern@gmail.com> Cc: Frederic Weisbecker <fweisbec@gmail.com> Cc: Jiri Olsa <jolsa@redhat.com> Cc: Mike Galbraith <efault@gmx.de> Cc: Namhyung Kim <namhyung@gmail.com> Cc: Paul Mackerras <paulus@samba.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Stephane Eranian <eranian@google.com> Cc: Steven Rostedt <rostedt@goodmis.org> Cc: Thomas Gleixner <tglx@linutronix.de> Link: http://lkml.kernel.org/n/tip-v9x3q9rv4caxtox7wtjpchq5@git.kernel.org Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools/perf/builtin-trace.c')
-rw-r--r--tools/perf/builtin-trace.c300
1 files changed, 300 insertions, 0 deletions
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
new file mode 100644
index 000000000000..5fa1820cc969
--- /dev/null
+++ b/tools/perf/builtin-trace.c
@@ -0,0 +1,300 @@
1#include "builtin.h"
2#include "util/evlist.h"
3#include "util/parse-options.h"
4#include "util/thread_map.h"
5#include "event-parse.h"
6
7#include <libaudit.h>
8#include <stdlib.h>
9
10static struct syscall_fmt {
11 const char *name;
12 bool errmsg;
13 bool timeout;
14} syscall_fmts[] = {
15 { .name = "futex", .errmsg = true, },
16 { .name = "poll", .errmsg = true, .timeout = true, },
17 { .name = "ppoll", .errmsg = true, .timeout = true, },
18 { .name = "read", .errmsg = true, },
19 { .name = "recvfrom", .errmsg = true, },
20 { .name = "select", .errmsg = true, .timeout = true, },
21};
22
23static int syscall_fmt__cmp(const void *name, const void *fmtp)
24{
25 const struct syscall_fmt *fmt = fmtp;
26 return strcmp(name, fmt->name);
27}
28
29static struct syscall_fmt *syscall_fmt__find(const char *name)
30{
31 const int nmemb = ARRAY_SIZE(syscall_fmts);
32 return bsearch(name, syscall_fmts, nmemb, sizeof(struct syscall_fmt), syscall_fmt__cmp);
33}
34
35struct syscall {
36 struct event_format *tp_format;
37 const char *name;
38 struct syscall_fmt *fmt;
39};
40
41struct trace {
42 int audit_machine;
43 struct {
44 int max;
45 struct syscall *table;
46 } syscalls;
47 struct perf_record_opts opts;
48};
49
50static int trace__read_syscall_info(struct trace *trace, int id)
51{
52 char tp_name[128];
53 struct syscall *sc;
54
55 if (id > trace->syscalls.max) {
56 struct syscall *nsyscalls = realloc(trace->syscalls.table, (id + 1) * sizeof(*sc));
57
58 if (nsyscalls == NULL)
59 return -1;
60
61 if (trace->syscalls.max != -1) {
62 memset(nsyscalls + trace->syscalls.max + 1, 0,
63 (id - trace->syscalls.max) * sizeof(*sc));
64 } else {
65 memset(nsyscalls, 0, (id + 1) * sizeof(*sc));
66 }
67
68 trace->syscalls.table = nsyscalls;
69 trace->syscalls.max = id;
70 }
71
72 sc = trace->syscalls.table + id;
73 sc->name = audit_syscall_to_name(id, trace->audit_machine);
74 if (sc->name == NULL)
75 return -1;
76
77 snprintf(tp_name, sizeof(tp_name), "sys_enter_%s", sc->name);
78
79 sc->tp_format = event_format__new("syscalls", tp_name);
80 sc->fmt = syscall_fmt__find(sc->name);
81
82 return sc->tp_format != NULL ? 0 : -1;
83}
84
85static size_t syscall__fprintf_args(struct syscall *sc, unsigned long *args, FILE *fp)
86{
87 int i = 0;
88 size_t printed = 0;
89
90 if (sc->tp_format != NULL) {
91 struct format_field *field;
92
93 for (field = sc->tp_format->format.fields->next; field; field = field->next) {
94 printed += fprintf(fp, "%s%s: %ld", printed ? ", " : "",
95 field->name, args[i++]);
96 }
97 } else {
98 while (i < 6) {
99 printed += fprintf(fp, "%sarg%d: %ld", printed ? ", " : "", i, args[i]);
100 ++i;
101 }
102 }
103
104 return printed;
105}
106
107static int trace__run(struct trace *trace)
108{
109 struct perf_evlist *evlist = perf_evlist__new(NULL, NULL);
110 struct perf_evsel *evsel, *evsel_enter, *evsel_exit;
111 int err = -1, i, nr_events = 0, before;
112
113 if (evlist == NULL) {
114 printf("Not enough memory to run!\n");
115 goto out;
116 }
117
118 evsel_enter = perf_evsel__newtp("raw_syscalls", "sys_enter", 0);
119 if (evsel_enter == NULL) {
120 printf("Couldn't read the raw_syscalls:sys_enter tracepoint information!\n");
121 goto out_delete_evlist;
122 }
123
124 perf_evlist__add(evlist, evsel_enter);
125
126 evsel_exit = perf_evsel__newtp("raw_syscalls", "sys_exit", 1);
127 if (evsel_exit == NULL) {
128 printf("Couldn't read the raw_syscalls:sys_exit tracepoint information!\n");
129 goto out_delete_evlist;
130 }
131
132 perf_evlist__add(evlist, evsel_exit);
133
134 err = perf_evlist__create_maps(evlist, &trace->opts.target);
135 if (err < 0) {
136 printf("Problems parsing the target to trace, check your options!\n");
137 goto out_delete_evlist;
138 }
139
140 perf_evlist__config_attrs(evlist, &trace->opts);
141
142 err = perf_evlist__open(evlist);
143 if (err < 0) {
144 printf("Couldn't create the events: %s\n", strerror(errno));
145 goto out_delete_evlist;
146 }
147
148 err = perf_evlist__mmap(evlist, UINT_MAX, false);
149 if (err < 0) {
150 printf("Couldn't mmap the events: %s\n", strerror(errno));
151 goto out_delete_evlist;
152 }
153
154 perf_evlist__enable(evlist);
155again:
156 before = nr_events;
157
158 for (i = 0; i < evlist->nr_mmaps; i++) {
159 union perf_event *event;
160
161 while ((event = perf_evlist__mmap_read(evlist, i)) != NULL) {
162 const u32 type = event->header.type;
163 struct syscall *sc;
164 struct perf_sample sample;
165 int id;
166
167 ++nr_events;
168
169 switch (type) {
170 case PERF_RECORD_SAMPLE:
171 break;
172 case PERF_RECORD_LOST:
173 printf("LOST %" PRIu64 " events!\n", event->lost.lost);
174 continue;
175 default:
176 printf("Unexpected %s event, skipping...\n",
177 perf_event__name(type));
178 continue;
179 }
180
181 err = perf_evlist__parse_sample(evlist, event, &sample);
182 if (err) {
183 printf("Can't parse sample, err = %d, skipping...\n", err);
184 continue;
185 }
186
187 evsel = perf_evlist__id2evsel(evlist, sample.id);
188 if (evsel == NULL) {
189 printf("Unknown tp ID %" PRIu64 ", skipping...\n", sample.id);
190 continue;
191 }
192
193 id = perf_evsel__intval(evsel, &sample, "id");
194 if (id < 0) {
195 printf("Invalid syscall %d id, skipping...\n", id);
196 continue;
197 }
198
199 if ((id > trace->syscalls.max || trace->syscalls.table[id].name == NULL) &&
200 trace__read_syscall_info(trace, id))
201 continue;
202
203 if ((id > trace->syscalls.max || trace->syscalls.table[id].name == NULL))
204 continue;
205
206 sc = &trace->syscalls.table[id];
207
208 if (evlist->threads->map[0] == -1 || evlist->threads->nr > 1)
209 printf("%d ", sample.tid);
210
211 if (evsel == evsel_enter) {
212 void *args = perf_evsel__rawptr(evsel, &sample, "args");
213
214 printf("%s(", sc->name);
215 syscall__fprintf_args(sc, args, stdout);
216 } else if (evsel == evsel_exit) {
217 int ret = perf_evsel__intval(evsel, &sample, "ret");
218
219 if (ret < 0 && sc->fmt && sc->fmt->errmsg) {
220 char bf[256];
221 const char *emsg = strerror_r(-ret, bf, sizeof(bf)),
222 *e = audit_errno_to_name(-ret);
223
224 printf(") = -1 %s %s", e, emsg);
225 } else if (ret == 0 && sc->fmt && sc->fmt->timeout)
226 printf(") = 0 Timeout");
227 else
228 printf(") = %d", ret);
229
230 putchar('\n');
231 }
232 }
233 }
234
235 if (nr_events == before)
236 poll(evlist->pollfd, evlist->nr_fds, -1);
237
238 goto again;
239
240out_delete_evlist:
241 perf_evlist__delete(evlist);
242out:
243 return err;
244}
245
246int cmd_trace(int argc, const char **argv, const char *prefix __maybe_unused)
247{
248 const char * const trace_usage[] = {
249 "perf trace [<options>]",
250 NULL
251 };
252 struct trace trace = {
253 .audit_machine = audit_detect_machine(),
254 .syscalls = {
255 . max = -1,
256 },
257 .opts = {
258 .target = {
259 .uid = UINT_MAX,
260 .uses_mmap = true,
261 },
262 .user_freq = UINT_MAX,
263 .user_interval = ULLONG_MAX,
264 .no_delay = true,
265 .mmap_pages = 1024,
266 },
267 };
268 const struct option trace_options[] = {
269 OPT_STRING('p', "pid", &trace.opts.target.pid, "pid",
270 "trace events on existing process id"),
271 OPT_STRING(0, "tid", &trace.opts.target.tid, "tid",
272 "trace events on existing thread id"),
273 OPT_BOOLEAN(0, "all-cpus", &trace.opts.target.system_wide,
274 "system-wide collection from all CPUs"),
275 OPT_STRING(0, "cpu", &trace.opts.target.cpu_list, "cpu",
276 "list of cpus to monitor"),
277 OPT_BOOLEAN(0, "no-inherit", &trace.opts.no_inherit,
278 "child tasks do not inherit counters"),
279 OPT_UINTEGER(0, "mmap-pages", &trace.opts.mmap_pages,
280 "number of mmap data pages"),
281 OPT_STRING(0, "uid", &trace.opts.target.uid_str, "user",
282 "user to profile"),
283 OPT_END()
284 };
285 int err;
286
287 argc = parse_options(argc, argv, trace_options, trace_usage, 0);
288 if (argc)
289 usage_with_options(trace_usage, trace_options);
290
291 err = perf_target__parse_uid(&trace.opts.target);
292 if (err) {
293 char bf[BUFSIZ];
294 perf_target__strerror(&trace.opts.target, err, bf, sizeof(bf));
295 printf("%s", bf);
296 return err;
297 }
298
299 return trace__run(&trace);
300}