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 | } |
