]>
Commit | Line | Data |
---|---|---|
5f9c39dc FW |
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 | ||
956ffd02 TZ |
9 | static char const *script_name; |
10 | static char const *generate_script_lang; | |
11 | ||
12 | static int default_start_script(const char *script __attribute((unused))) | |
13 | { | |
14 | return 0; | |
15 | } | |
16 | ||
17 | static int default_stop_script(void) | |
18 | { | |
19 | return 0; | |
20 | } | |
21 | ||
22 | static int default_generate_script(const char *outfile __attribute ((unused))) | |
23 | { | |
24 | return 0; | |
25 | } | |
26 | ||
27 | static struct scripting_ops default_scripting_ops = { | |
28 | .start_script = default_start_script, | |
29 | .stop_script = default_stop_script, | |
30 | .process_event = print_event, | |
31 | .generate_script = default_generate_script, | |
32 | }; | |
33 | ||
34 | static struct scripting_ops *scripting_ops; | |
35 | ||
36 | static void setup_scripting(void) | |
37 | { | |
38 | /* make sure PERF_EXEC_PATH is set for scripts */ | |
39 | perf_set_argv_exec_path(perf_exec_path()); | |
40 | ||
16c632de TZ |
41 | setup_perl_scripting(); |
42 | ||
956ffd02 TZ |
43 | scripting_ops = &default_scripting_ops; |
44 | } | |
45 | ||
46 | static int cleanup_scripting(void) | |
47 | { | |
48 | return scripting_ops->stop_script(); | |
49 | } | |
50 | ||
5f9c39dc FW |
51 | #include "util/parse-options.h" |
52 | ||
53 | #include "perf.h" | |
54 | #include "util/debug.h" | |
55 | ||
56 | #include "util/trace-event.h" | |
016e92fb | 57 | #include "util/data_map.h" |
956ffd02 | 58 | #include "util/exec_cmd.h" |
5f9c39dc | 59 | |
956ffd02 | 60 | static char const *input_name = "perf.data"; |
5f9c39dc | 61 | |
956ffd02 TZ |
62 | static struct perf_header *header; |
63 | static u64 sample_type; | |
5f9c39dc | 64 | |
62daacb5 | 65 | static int process_sample_event(event_t *event) |
5f9c39dc | 66 | { |
5f9c39dc | 67 | u64 ip = event->ip.ip; |
6ddf259d | 68 | u64 timestamp = -1; |
cd6feeea | 69 | u32 cpu = -1; |
5f9c39dc FW |
70 | u64 period = 1; |
71 | void *more_data = event->ip.__more_data; | |
d5b889f2 | 72 | struct thread *thread = threads__findnew(event->ip.pid); |
5f9c39dc | 73 | |
6ddf259d IM |
74 | if (sample_type & PERF_SAMPLE_TIME) { |
75 | timestamp = *(u64 *)more_data; | |
76 | more_data += sizeof(u64); | |
77 | } | |
78 | ||
cd6feeea IM |
79 | if (sample_type & PERF_SAMPLE_CPU) { |
80 | cpu = *(u32 *)more_data; | |
81 | more_data += sizeof(u32); | |
82 | more_data += sizeof(u32); /* reserved */ | |
83 | } | |
84 | ||
5f9c39dc FW |
85 | if (sample_type & PERF_SAMPLE_PERIOD) { |
86 | period = *(u64 *)more_data; | |
87 | more_data += sizeof(u64); | |
88 | } | |
89 | ||
62daacb5 | 90 | dump_printf("(IP, %d): %d/%d: %p period: %Ld\n", |
5f9c39dc FW |
91 | event->header.misc, |
92 | event->ip.pid, event->ip.tid, | |
93 | (void *)(long)ip, | |
94 | (long long)period); | |
95 | ||
5f9c39dc | 96 | if (thread == NULL) { |
6beba7ad ACM |
97 | pr_debug("problem processing %d event, skipping it.\n", |
98 | event->header.type); | |
5f9c39dc FW |
99 | return -1; |
100 | } | |
101 | ||
f39cdf25 JL |
102 | dump_printf(" ... thread: %s:%d\n", thread->comm, thread->pid); |
103 | ||
5f9c39dc FW |
104 | if (sample_type & PERF_SAMPLE_RAW) { |
105 | struct { | |
106 | u32 size; | |
107 | char data[0]; | |
108 | } *raw = more_data; | |
109 | ||
110 | /* | |
111 | * FIXME: better resolve from pid from the struct trace_entry | |
112 | * field, although it should be the same than this perf | |
113 | * event pid | |
114 | */ | |
956ffd02 TZ |
115 | scripting_ops->process_event(cpu, raw->data, raw->size, |
116 | timestamp, thread->comm); | |
5f9c39dc | 117 | } |
62daacb5 | 118 | event__stats.total += period; |
5f9c39dc FW |
119 | |
120 | return 0; | |
121 | } | |
122 | ||
016e92fb | 123 | static int sample_type_check(u64 type) |
5f9c39dc | 124 | { |
016e92fb | 125 | sample_type = type; |
5f9c39dc | 126 | |
016e92fb FW |
127 | if (!(sample_type & PERF_SAMPLE_RAW)) { |
128 | fprintf(stderr, | |
129 | "No trace sample to read. Did you call perf record " | |
130 | "without -R?"); | |
5f9c39dc FW |
131 | return -1; |
132 | } | |
133 | ||
134 | return 0; | |
135 | } | |
136 | ||
016e92fb FW |
137 | static struct perf_file_handler file_handler = { |
138 | .process_sample_event = process_sample_event, | |
62daacb5 | 139 | .process_comm_event = event__process_comm, |
016e92fb FW |
140 | .sample_type_check = sample_type_check, |
141 | }; | |
142 | ||
5f9c39dc FW |
143 | static int __cmd_trace(void) |
144 | { | |
d5b889f2 | 145 | register_idle_thread(); |
016e92fb | 146 | register_perf_file_handler(&file_handler); |
5f9c39dc | 147 | |
b32d133a | 148 | return mmap_dispatch_perf_file(&header, input_name, |
62daacb5 | 149 | 0, 0, &event__cwdlen, &event__cwd); |
5f9c39dc FW |
150 | } |
151 | ||
956ffd02 TZ |
152 | struct script_spec { |
153 | struct list_head node; | |
154 | struct scripting_ops *ops; | |
155 | char spec[0]; | |
156 | }; | |
157 | ||
158 | LIST_HEAD(script_specs); | |
159 | ||
160 | static struct script_spec *script_spec__new(const char *spec, | |
161 | struct scripting_ops *ops) | |
162 | { | |
163 | struct script_spec *s = malloc(sizeof(*s) + strlen(spec) + 1); | |
164 | ||
165 | if (s != NULL) { | |
166 | strcpy(s->spec, spec); | |
167 | s->ops = ops; | |
168 | } | |
169 | ||
170 | return s; | |
171 | } | |
172 | ||
173 | static void script_spec__delete(struct script_spec *s) | |
174 | { | |
175 | free(s->spec); | |
176 | free(s); | |
177 | } | |
178 | ||
179 | static void script_spec__add(struct script_spec *s) | |
180 | { | |
181 | list_add_tail(&s->node, &script_specs); | |
182 | } | |
183 | ||
184 | static struct script_spec *script_spec__find(const char *spec) | |
185 | { | |
186 | struct script_spec *s; | |
187 | ||
188 | list_for_each_entry(s, &script_specs, node) | |
189 | if (strcasecmp(s->spec, spec) == 0) | |
190 | return s; | |
191 | return NULL; | |
192 | } | |
193 | ||
194 | static struct script_spec *script_spec__findnew(const char *spec, | |
195 | struct scripting_ops *ops) | |
196 | { | |
197 | struct script_spec *s = script_spec__find(spec); | |
198 | ||
199 | if (s) | |
200 | return s; | |
201 | ||
202 | s = script_spec__new(spec, ops); | |
203 | if (!s) | |
204 | goto out_delete_spec; | |
205 | ||
206 | script_spec__add(s); | |
207 | ||
208 | return s; | |
209 | ||
210 | out_delete_spec: | |
211 | script_spec__delete(s); | |
212 | ||
213 | return NULL; | |
214 | } | |
215 | ||
216 | int script_spec_register(const char *spec, struct scripting_ops *ops) | |
217 | { | |
218 | struct script_spec *s; | |
219 | ||
220 | s = script_spec__find(spec); | |
221 | if (s) | |
222 | return -1; | |
223 | ||
224 | s = script_spec__findnew(spec, ops); | |
225 | if (!s) | |
226 | return -1; | |
227 | ||
228 | return 0; | |
229 | } | |
230 | ||
231 | static struct scripting_ops *script_spec__lookup(const char *spec) | |
232 | { | |
233 | struct script_spec *s = script_spec__find(spec); | |
234 | if (!s) | |
235 | return NULL; | |
236 | ||
237 | return s->ops; | |
238 | } | |
239 | ||
240 | static void list_available_languages(void) | |
241 | { | |
242 | struct script_spec *s; | |
243 | ||
244 | fprintf(stderr, "\n"); | |
245 | fprintf(stderr, "Scripting language extensions (used in " | |
246 | "perf trace -s [spec:]script.[spec]):\n\n"); | |
247 | ||
248 | list_for_each_entry(s, &script_specs, node) | |
249 | fprintf(stderr, " %-42s [%s]\n", s->spec, s->ops->name); | |
250 | ||
251 | fprintf(stderr, "\n"); | |
252 | } | |
253 | ||
254 | static int parse_scriptname(const struct option *opt __used, | |
255 | const char *str, int unset __used) | |
256 | { | |
257 | char spec[PATH_MAX]; | |
258 | const char *script, *ext; | |
259 | int len; | |
260 | ||
261 | if (strcmp(str, "list") == 0) { | |
262 | list_available_languages(); | |
263 | return 0; | |
264 | } | |
265 | ||
266 | script = strchr(str, ':'); | |
267 | if (script) { | |
268 | len = script - str; | |
269 | if (len >= PATH_MAX) { | |
270 | fprintf(stderr, "invalid language specifier"); | |
271 | return -1; | |
272 | } | |
273 | strncpy(spec, str, len); | |
274 | spec[len] = '\0'; | |
275 | scripting_ops = script_spec__lookup(spec); | |
276 | if (!scripting_ops) { | |
277 | fprintf(stderr, "invalid language specifier"); | |
278 | return -1; | |
279 | } | |
280 | script++; | |
281 | } else { | |
282 | script = str; | |
283 | ext = strchr(script, '.'); | |
284 | if (!ext) { | |
285 | fprintf(stderr, "invalid script extension"); | |
286 | return -1; | |
287 | } | |
288 | scripting_ops = script_spec__lookup(++ext); | |
289 | if (!scripting_ops) { | |
290 | fprintf(stderr, "invalid script extension"); | |
291 | return -1; | |
292 | } | |
293 | } | |
294 | ||
295 | script_name = strdup(script); | |
296 | ||
297 | return 0; | |
298 | } | |
299 | ||
5f9c39dc FW |
300 | static const char * const annotate_usage[] = { |
301 | "perf trace [<options>] <command>", | |
302 | NULL | |
303 | }; | |
304 | ||
305 | static const struct option options[] = { | |
306 | OPT_BOOLEAN('D', "dump-raw-trace", &dump_trace, | |
307 | "dump raw trace in ASCII"), | |
308 | OPT_BOOLEAN('v', "verbose", &verbose, | |
309 | "be more verbose (show symbol address, etc)"), | |
cda48461 SR |
310 | OPT_BOOLEAN('l', "latency", &latency_format, |
311 | "show latency attributes (irqs/preemption disabled, etc)"), | |
956ffd02 TZ |
312 | OPT_CALLBACK('s', "script", NULL, "name", |
313 | "script file name (lang:script name, script name, or *)", | |
314 | parse_scriptname), | |
315 | OPT_STRING('g', "gen-script", &generate_script_lang, "lang", | |
316 | "generate perf-trace.xx script in specified language"), | |
317 | ||
1909629f | 318 | OPT_END() |
5f9c39dc FW |
319 | }; |
320 | ||
321 | int cmd_trace(int argc, const char **argv, const char *prefix __used) | |
322 | { | |
956ffd02 TZ |
323 | int err; |
324 | ||
00a192b3 | 325 | symbol__init(0); |
5f9c39dc | 326 | |
956ffd02 TZ |
327 | setup_scripting(); |
328 | ||
5f9c39dc FW |
329 | argc = parse_options(argc, argv, options, annotate_usage, 0); |
330 | if (argc) { | |
331 | /* | |
332 | * Special case: if there's an argument left then assume tha | |
333 | * it's a symbol filter: | |
334 | */ | |
335 | if (argc > 1) | |
336 | usage_with_options(annotate_usage, options); | |
337 | } | |
338 | ||
5f9c39dc FW |
339 | setup_pager(); |
340 | ||
956ffd02 TZ |
341 | if (generate_script_lang) { |
342 | struct stat perf_stat; | |
343 | ||
344 | int input = open(input_name, O_RDONLY); | |
345 | if (input < 0) { | |
346 | perror("failed to open file"); | |
347 | exit(-1); | |
348 | } | |
349 | ||
350 | err = fstat(input, &perf_stat); | |
351 | if (err < 0) { | |
352 | perror("failed to stat file"); | |
353 | exit(-1); | |
354 | } | |
355 | ||
356 | if (!perf_stat.st_size) { | |
357 | fprintf(stderr, "zero-sized file, nothing to do!\n"); | |
358 | exit(0); | |
359 | } | |
360 | ||
361 | scripting_ops = script_spec__lookup(generate_script_lang); | |
362 | if (!scripting_ops) { | |
363 | fprintf(stderr, "invalid language specifier"); | |
364 | return -1; | |
365 | } | |
366 | ||
367 | header = perf_header__new(); | |
368 | if (header == NULL) | |
369 | return -1; | |
370 | ||
371 | perf_header__read(header, input); | |
372 | err = scripting_ops->generate_script("perf-trace"); | |
373 | goto out; | |
374 | } | |
375 | ||
376 | if (script_name) { | |
377 | err = scripting_ops->start_script(script_name); | |
378 | if (err) | |
379 | goto out; | |
380 | } | |
381 | ||
382 | err = __cmd_trace(); | |
383 | ||
384 | cleanup_scripting(); | |
385 | out: | |
386 | return err; | |
5f9c39dc | 387 | } |