diff options
author | Steven Rostedt <srostedt@redhat.com> | 2010-02-17 15:35:56 -0500 |
---|---|---|
committer | Steven Rostedt <rostedt@goodmis.org> | 2010-02-17 15:35:56 -0500 |
commit | 85da60357f4b515af1c7be362cf0465f65672fd6 (patch) | |
tree | db68bdbd6d4fdb09c860e1f3c11410b7d4dae359 | |
parent | 4991c35c3bab5e000917ad41a0277008f18c4ab3 (diff) |
trace-cmd: Add trace-cmd read -w to show wakeup latencies
Add the -w option to trace-cmd read to record and display
wakeup latencies.
Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
-rw-r--r-- | trace-cmd.c | 1 | ||||
-rw-r--r-- | trace-hash-local.h | 52 | ||||
-rw-r--r-- | trace-hash.c | 28 | ||||
-rw-r--r-- | trace-hash.h | 3 | ||||
-rw-r--r-- | trace-read.c | 207 |
5 files changed, 260 insertions, 31 deletions
diff --git a/trace-cmd.c b/trace-cmd.c index c8e507a..d91e212 100644 --- a/trace-cmd.c +++ b/trace-cmd.c | |||
@@ -1043,6 +1043,7 @@ void usage(char **argv) | |||
1043 | " -E show event files stored\n" | 1043 | " -E show event files stored\n" |
1044 | " -F filter to filter output on\n" | 1044 | " -F filter to filter output on\n" |
1045 | " -v will negate all -F after it (Not show matches)\n" | 1045 | " -v will negate all -F after it (Not show matches)\n" |
1046 | " -w show wakeup latencies\n" | ||
1046 | " -l show latency format (default with latency tracers)\n" | 1047 | " -l show latency format (default with latency tracers)\n" |
1047 | "\n" | 1048 | "\n" |
1048 | " %s split [options] -o file [start [end]]\n" | 1049 | " %s split [options] -o file [start [end]]\n" |
diff --git a/trace-hash-local.h b/trace-hash-local.h new file mode 100644 index 0000000..9ddd5ae --- /dev/null +++ b/trace-hash-local.h | |||
@@ -0,0 +1,52 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2009, Steven Rostedt <srostedt@redhat.com> | ||
3 | * | ||
4 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; version 2 of the License (not later!) | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
18 | * | ||
19 | * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
20 | */ | ||
21 | #ifndef _TRACE_HASH_LOCAL_H | ||
22 | #define _TRACE_HASH_LOCAL_H | ||
23 | |||
24 | static inline unsigned int trace_hash(int val) | ||
25 | { | ||
26 | int hash, tmp; | ||
27 | |||
28 | hash = 12546869; /* random prime */ | ||
29 | |||
30 | /* | ||
31 | * The following hash is based off of Paul Hsieh's super fast hash: | ||
32 | * http://www.azillionmonkeys.com/qed/hash.html | ||
33 | * Note, he released this code unde the GPL 2.0 license, which | ||
34 | * is the same as the license for the programs that use it here. | ||
35 | */ | ||
36 | |||
37 | hash += (val & 0xffff); | ||
38 | tmp = (val >> 16) ^ hash; | ||
39 | hash = (hash << 16) ^ tmp; | ||
40 | hash += hash >> 11; | ||
41 | |||
42 | hash ^= hash << 3; | ||
43 | hash += hash >> 5; | ||
44 | hash ^= hash << 4; | ||
45 | hash += hash >> 17; | ||
46 | hash ^= hash << 25; | ||
47 | hash += hash >> 6; | ||
48 | |||
49 | return hash; | ||
50 | } | ||
51 | |||
52 | #endif /* _TRACE_HASH_LOCAL_H */ | ||
diff --git a/trace-hash.c b/trace-hash.c index 93da360..1197913 100644 --- a/trace-hash.c +++ b/trace-hash.c | |||
@@ -26,34 +26,6 @@ | |||
26 | 26 | ||
27 | #define FILTER_TASK_HASH_SIZE 256 | 27 | #define FILTER_TASK_HASH_SIZE 256 |
28 | 28 | ||
29 | guint trace_hash(gint val) | ||
30 | { | ||
31 | gint hash, tmp; | ||
32 | |||
33 | hash = 12546869; /* random prime */ | ||
34 | |||
35 | /* | ||
36 | * The following hash is based off of Paul Hsieh's super fast hash: | ||
37 | * http://www.azillionmonkeys.com/qed/hash.html | ||
38 | * Note, he released this code unde the GPL 2.0 license, which | ||
39 | * is the same as the license for the programs that use it here. | ||
40 | */ | ||
41 | |||
42 | hash += (val & 0xffff); | ||
43 | tmp = (val >> 16) ^ hash; | ||
44 | hash = (hash << 16) ^ tmp; | ||
45 | hash += hash >> 11; | ||
46 | |||
47 | hash ^= hash << 3; | ||
48 | hash += hash >> 5; | ||
49 | hash ^= hash << 4; | ||
50 | hash += hash >> 17; | ||
51 | hash ^= hash << 25; | ||
52 | hash += hash >> 6; | ||
53 | |||
54 | return hash; | ||
55 | } | ||
56 | |||
57 | struct filter_task_item * | 29 | struct filter_task_item * |
58 | filter_task_find_pid(struct filter_task *hash, gint pid) | 30 | filter_task_find_pid(struct filter_task *hash, gint pid) |
59 | { | 31 | { |
diff --git a/trace-hash.h b/trace-hash.h index b766073..1aaa828 100644 --- a/trace-hash.h +++ b/trace-hash.h | |||
@@ -22,6 +22,7 @@ | |||
22 | #define _TRACE_HASH_H | 22 | #define _TRACE_HASH_H |
23 | 23 | ||
24 | #include <glib.h> | 24 | #include <glib.h> |
25 | #include "trace-hash-local.h" | ||
25 | 26 | ||
26 | struct filter_task_item { | 27 | struct filter_task_item { |
27 | struct filter_task_item *next; | 28 | struct filter_task_item *next; |
@@ -33,8 +34,6 @@ struct filter_task { | |||
33 | gint count; | 34 | gint count; |
34 | }; | 35 | }; |
35 | 36 | ||
36 | guint trace_hash(gint val); | ||
37 | |||
38 | struct filter_task_item * | 37 | struct filter_task_item * |
39 | filter_task_find_pid(struct filter_task *hash, gint pid); | 38 | filter_task_find_pid(struct filter_task *hash, gint pid); |
40 | void filter_task_add_pid(struct filter_task *hash, gint pid); | 39 | void filter_task_add_pid(struct filter_task *hash, gint pid); |
diff --git a/trace-read.c b/trace-read.c index ee56530..0a1fd0a 100644 --- a/trace-read.c +++ b/trace-read.c | |||
@@ -37,6 +37,7 @@ | |||
37 | #include <errno.h> | 37 | #include <errno.h> |
38 | 38 | ||
39 | #include "trace-local.h" | 39 | #include "trace-local.h" |
40 | #include "trace-hash-local.h" | ||
40 | 41 | ||
41 | static struct filter { | 42 | static struct filter { |
42 | struct filter *next; | 43 | struct filter *next; |
@@ -54,6 +55,29 @@ const char *input_file = "trace.dat"; | |||
54 | 55 | ||
55 | static int filter_cpu = -1; | 56 | static int filter_cpu = -1; |
56 | 57 | ||
58 | static int show_wakeup; | ||
59 | static int wakeup_id; | ||
60 | static int wakeup_new_id; | ||
61 | static int sched_id; | ||
62 | |||
63 | static struct format_field *wakeup_task; | ||
64 | static struct format_field *wakeup_success; | ||
65 | static struct format_field *wakeup_new_task; | ||
66 | static struct format_field *wakeup_new_success; | ||
67 | static struct format_field *sched_task; | ||
68 | |||
69 | static unsigned long long total_wakeup_lat; | ||
70 | static unsigned long wakeup_lat_count; | ||
71 | |||
72 | struct wakeup_info { | ||
73 | struct wakeup_info *next; | ||
74 | unsigned long long start; | ||
75 | int pid; | ||
76 | }; | ||
77 | |||
78 | #define WAKEUP_HASH_SIZE 1024 | ||
79 | static struct wakeup_info *wakeup_hash[WAKEUP_HASH_SIZE]; | ||
80 | |||
57 | /* Debug variables for testing tracecmd_read_at */ | 81 | /* Debug variables for testing tracecmd_read_at */ |
58 | #define TEST_READ_AT 0 | 82 | #define TEST_READ_AT 0 |
59 | #if TEST_READ_AT | 83 | #if TEST_READ_AT |
@@ -250,6 +274,178 @@ static int filter_record(struct tracecmd_input *handle, | |||
250 | return 0; | 274 | return 0; |
251 | } | 275 | } |
252 | 276 | ||
277 | static void init_wakeup(struct tracecmd_input *handle) | ||
278 | { | ||
279 | struct event_format *event; | ||
280 | struct pevent *pevent; | ||
281 | |||
282 | if (!show_wakeup) | ||
283 | return; | ||
284 | |||
285 | pevent = tracecmd_get_pevent(handle); | ||
286 | |||
287 | event = pevent_find_event_by_name(pevent, "sched", "sched_wakeup"); | ||
288 | if (!event) | ||
289 | goto fail; | ||
290 | wakeup_id = event->id; | ||
291 | wakeup_task = pevent_find_field(event, "pid"); | ||
292 | if (!wakeup_task) | ||
293 | goto fail; | ||
294 | wakeup_success = pevent_find_field(event, "success"); | ||
295 | if (!wakeup_success) | ||
296 | goto fail; | ||
297 | |||
298 | |||
299 | event = pevent_find_event_by_name(pevent, "sched", "sched_switch"); | ||
300 | if (!event) | ||
301 | goto fail; | ||
302 | sched_id = event->id; | ||
303 | sched_task = pevent_find_field(event, "next_pid"); | ||
304 | if (!sched_task) | ||
305 | goto fail; | ||
306 | |||
307 | |||
308 | wakeup_new_id = -1; | ||
309 | |||
310 | event = pevent_find_event_by_name(pevent, "sched", "sched_wakeup_new"); | ||
311 | if (!event) | ||
312 | goto skip; | ||
313 | wakeup_new_id = event->id; | ||
314 | wakeup_new_task = pevent_find_field(event, "pid"); | ||
315 | if (!wakeup_new_task) | ||
316 | goto fail; | ||
317 | wakeup_new_success = pevent_find_field(event, "success"); | ||
318 | if (!wakeup_new_success) | ||
319 | goto fail; | ||
320 | |||
321 | skip: | ||
322 | return; | ||
323 | |||
324 | fail: | ||
325 | show_wakeup = 0; | ||
326 | } | ||
327 | |||
328 | static unsigned int calc_wakeup_key(unsigned long val) | ||
329 | { | ||
330 | return trace_hash(val) % WAKEUP_HASH_SIZE; | ||
331 | } | ||
332 | |||
333 | static struct wakeup_info * | ||
334 | __find_wakeup(unsigned int key, unsigned int val) | ||
335 | { | ||
336 | struct wakeup_info *info = wakeup_hash[key]; | ||
337 | |||
338 | while (info) { | ||
339 | if (info->pid == val) | ||
340 | return info; | ||
341 | } | ||
342 | |||
343 | return NULL; | ||
344 | } | ||
345 | |||
346 | static void add_wakeup(unsigned int val, unsigned long long start) | ||
347 | { | ||
348 | unsigned int key = calc_wakeup_key(val); | ||
349 | struct wakeup_info *info; | ||
350 | |||
351 | info = __find_wakeup(key, val); | ||
352 | if (info) { | ||
353 | /* Hmm, double wakeup? */ | ||
354 | info->start = start; | ||
355 | return; | ||
356 | } | ||
357 | |||
358 | info = malloc_or_die(sizeof(*info)); | ||
359 | info->pid = val; | ||
360 | info->start = start; | ||
361 | info->next = wakeup_hash[key]; | ||
362 | wakeup_hash[key] = info; | ||
363 | } | ||
364 | |||
365 | static void add_sched(unsigned int val, unsigned long long end) | ||
366 | { | ||
367 | unsigned int key = calc_wakeup_key(val); | ||
368 | struct wakeup_info *info; | ||
369 | struct wakeup_info **next; | ||
370 | unsigned long long cal; | ||
371 | |||
372 | info = __find_wakeup(key, val); | ||
373 | if (!info) | ||
374 | return; | ||
375 | |||
376 | cal = end - info->start; | ||
377 | |||
378 | printf(" Latency: %llu.%03llu usecs", cal / 1000, cal % 1000); | ||
379 | |||
380 | total_wakeup_lat += cal; | ||
381 | wakeup_lat_count++; | ||
382 | |||
383 | next = &wakeup_hash[key]; | ||
384 | while (*next) { | ||
385 | if (*next == info) { | ||
386 | *next = info->next; | ||
387 | break; | ||
388 | } | ||
389 | next = &(*next)->next; | ||
390 | } | ||
391 | free(info); | ||
392 | } | ||
393 | |||
394 | static void process_wakeup(struct pevent *pevent, struct record *record) | ||
395 | { | ||
396 | unsigned long long val; | ||
397 | int id; | ||
398 | |||
399 | if (!show_wakeup) | ||
400 | return; | ||
401 | |||
402 | id = pevent_data_type(pevent, record); | ||
403 | if (id == wakeup_id) { | ||
404 | if (pevent_read_number_field(wakeup_success, record->data, &val)) | ||
405 | return; | ||
406 | if (!val) | ||
407 | return; | ||
408 | if (pevent_read_number_field(wakeup_task, record->data, &val)) | ||
409 | return; | ||
410 | add_wakeup(val, record->ts); | ||
411 | } else if (id == wakeup_new_id) { | ||
412 | if (pevent_read_number_field(wakeup_new_success, record->data, &val)) | ||
413 | return; | ||
414 | if (!val) | ||
415 | return; | ||
416 | if (pevent_read_number_field(wakeup_new_task, record->data, &val)) | ||
417 | return; | ||
418 | add_wakeup(val, record->ts); | ||
419 | } else if (id == sched_id) { | ||
420 | if (pevent_read_number_field(sched_task, record->data, &val)) | ||
421 | return; | ||
422 | add_sched(val, record->ts); | ||
423 | } | ||
424 | } | ||
425 | |||
426 | static void finish_wakeup(void) | ||
427 | { | ||
428 | struct wakeup_info *info; | ||
429 | int i; | ||
430 | |||
431 | if (!show_wakeup || !wakeup_lat_count) | ||
432 | return; | ||
433 | |||
434 | total_wakeup_lat /= wakeup_lat_count; | ||
435 | |||
436 | printf("\nAverage wakeup latency: %llu.%03llu usecs\n\n", | ||
437 | total_wakeup_lat / 1000, | ||
438 | total_wakeup_lat % 1000); | ||
439 | |||
440 | for (i = 0; i < WAKEUP_HASH_SIZE; i++) { | ||
441 | while (wakeup_hash[i]) { | ||
442 | info = wakeup_hash[i]; | ||
443 | wakeup_hash[i] = info->next; | ||
444 | free(info); | ||
445 | } | ||
446 | } | ||
447 | } | ||
448 | |||
253 | static void show_data(struct tracecmd_input *handle, | 449 | static void show_data(struct tracecmd_input *handle, |
254 | struct record *record, int cpu) | 450 | struct record *record, int cpu) |
255 | { | 451 | { |
@@ -266,6 +462,9 @@ static void show_data(struct tracecmd_input *handle, | |||
266 | trace_seq_init(&s); | 462 | trace_seq_init(&s); |
267 | pevent_print_event(pevent, &s, record); | 463 | pevent_print_event(pevent, &s, record); |
268 | trace_seq_do_printf(&s); | 464 | trace_seq_do_printf(&s); |
465 | |||
466 | process_wakeup(pevent, record); | ||
467 | |||
269 | printf("\n"); | 468 | printf("\n"); |
270 | } | 469 | } |
271 | 470 | ||
@@ -305,6 +504,7 @@ static void read_data_info(struct tracecmd_input *handle) | |||
305 | return; | 504 | return; |
306 | } | 505 | } |
307 | 506 | ||
507 | init_wakeup(handle); | ||
308 | process_filters(handle); | 508 | process_filters(handle); |
309 | 509 | ||
310 | do { | 510 | do { |
@@ -374,7 +574,7 @@ void trace_report (int argc, char **argv) | |||
374 | {NULL, 0, NULL, 0} | 574 | {NULL, 0, NULL, 0} |
375 | }; | 575 | }; |
376 | 576 | ||
377 | c = getopt_long (argc-1, argv+1, "+hi:fepPlEF:v", | 577 | c = getopt_long (argc-1, argv+1, "+hi:fepPlEwF:v", |
378 | long_options, &option_index); | 578 | long_options, &option_index); |
379 | if (c == -1) | 579 | if (c == -1) |
380 | break; | 580 | break; |
@@ -403,6 +603,9 @@ void trace_report (int argc, char **argv) | |||
403 | case 'E': | 603 | case 'E': |
404 | show_events = 1; | 604 | show_events = 1; |
405 | break; | 605 | break; |
606 | case 'w': | ||
607 | show_wakeup = 1; | ||
608 | break; | ||
406 | case 'l': | 609 | case 'l': |
407 | latency_format = 1; | 610 | latency_format = 1; |
408 | break; | 611 | break; |
@@ -489,5 +692,7 @@ void trace_report (int argc, char **argv) | |||
489 | 692 | ||
490 | tracecmd_close(handle); | 693 | tracecmd_close(handle); |
491 | 694 | ||
695 | finish_wakeup(); | ||
696 | |||
492 | return; | 697 | return; |
493 | } | 698 | } |