Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf kwork: Implement perf kwork timehist

Implements framework of perf kwork timehist,
to provide an analysis of kernel work events.

Test cases:

# perf kwork tim
Runtime start Runtime end Cpu Kwork name Runtime Delaytime
(TYPE)NAME:NUM (msec) (msec)
----------------- ----------------- ------ ------------------------------ ---------- ----------
91576.060290 91576.060344 [0000] (s)RCU:9 0.055 0.111
91576.061470 91576.061547 [0000] (s)SCHED:7 0.077 0.073
91576.062604 91576.062697 [0001] (s)RCU:9 0.094 0.409
91576.064443 91576.064517 [0002] (s)RCU:9 0.074 0.114
91576.065144 91576.065211 [0000] (s)SCHED:7 0.067 0.058
91576.066564 91576.066609 [0003] (s)RCU:9 0.045 0.110
91576.068495 91576.068559 [0000] (s)SCHED:7 0.064 0.059
91576.068900 91576.068996 [0004] (s)RCU:9 0.096 0.726
91576.069364 91576.069420 [0002] (s)RCU:9 0.056 0.082
91576.069649 91576.069701 [0004] (s)RCU:9 0.052 0.111
91576.070147 91576.070206 [0000] (s)SCHED:7 0.060 0.057
91576.073147 91576.073202 [0000] (s)SCHED:7 0.054 0.060
<SNIP>

# perf kwork tim --max-stack 2 -g
Runtime start Runtime end Cpu Kwork name Runtime Delaytime
(TYPE)NAME:NUM (msec) (msec)
----------------- ----------------- ------ ------------------------------ ---------- ----------
91576.060290 91576.060344 [0000] (s)RCU:9 0.055 0.111 irq_exit_rcu <- sysvec_apic_timer_interrupt
91576.061470 91576.061547 [0000] (s)SCHED:7 0.077 0.073 irq_exit_rcu <- sysvec_call_function_single
91576.062604 91576.062697 [0001] (s)RCU:9 0.094 0.409 irq_exit_rcu <- sysvec_apic_timer_interrupt
91576.064443 91576.064517 [0002] (s)RCU:9 0.074 0.114 irq_exit_rcu <- sysvec_apic_timer_interrupt
91576.065144 91576.065211 [0000] (s)SCHED:7 0.067 0.058 irq_exit_rcu <- sysvec_call_function_single
91576.066564 91576.066609 [0003] (s)RCU:9 0.045 0.110 irq_exit_rcu <- sysvec_apic_timer_interrupt
91576.068495 91576.068559 [0000] (s)SCHED:7 0.064 0.059 irq_exit_rcu <- sysvec_call_function_single
91576.068900 91576.068996 [0004] (s)RCU:9 0.096 0.726 irq_exit_rcu <- sysvec_apic_timer_interrupt
91576.069364 91576.069420 [0002] (s)RCU:9 0.056 0.082 irq_exit_rcu <- sysvec_apic_timer_interrupt
91576.069649 91576.069701 [0004] (s)RCU:9 0.052 0.111 irq_exit_rcu <- sysvec_apic_timer_interrupt
<SNIP>

Committer testing:

# perf kwork -k workqueue timehist | head -40
Runtime start Runtime end Cpu Kwork name Runtime Delaytime
(TYPE)NAME:NUM (msec) (msec)
----------------- ----------------- ------ ------------------------------ ---------- ----------
26520.211825 26520.211832 [0019] (w)free_work 0.007 0.004
26520.212929 26520.212934 [0020] (w)free_work 0.005 0.004
26520.213226 26520.213228 [0014] (w)kfree_rcu_work 0.002 0.004
26520.214057 26520.214061 [0021] (w)free_work 0.004 0.004
26520.221239 26520.221241 [0007] (w)kfree_rcu_work 0.002 0.009
26520.223232 26520.223238 [0013] (w)psi_avgs_work 0.005 0.006
26520.230057 26520.230060 [0020] (w)free_work 0.003 0.003
26520.270428 26520.270434 [0015] (w)free_work 0.006 0.004
26520.270546 26520.270550 [0014] (w)free_work 0.004 0.003
26520.281626 26520.281629 [0015] (w)free_work 0.003 0.002
26520.287225 26520.287230 [0012] (w)psi_avgs_work 0.005 0.008
26520.287231 26520.287235 [0001] (w)psi_avgs_work 0.004 0.011
26520.287236 26520.287239 [0001] (w)psi_avgs_work 0.003 0.012
26520.329488 26520.329492 [0024] (w)free_work 0.004 0.004
26520.330600 26520.330605 [0007] (w)free_work 0.005 0.004
26520.334218 26520.334218 [0007] (w)kfree_rcu_monitor 0.001 0.002
26520.335220 26520.335221 [0005] (w)kfree_rcu_monitor 0.001 0.004
26520.343980 26520.343985 [0007] (w)free_work 0.005 0.002
26520.345093 26520.345097 [0006] (w)free_work 0.004 0.003
26520.351233 26520.351238 [0027] (w)psi_avgs_work 0.005 0.008
26520.353228 26520.353229 [0007] (w)kfree_rcu_work 0.001 0.002
26520.353229 26520.353231 [0005] (w)kfree_rcu_work 0.001 0.006
26520.382381 26520.382383 [0006] (w)free_work 0.003 0.002
26520.386547 26520.386548 [0006] (w)free_work 0.002 0.001
26520.391243 26520.391245 [0015] (w)console_callback 0.002 0.016
26520.415369 26520.415621 [0027] (w)btrfs_work_helper 0.252
26520.415351 26520.416174 [0002] (w)btrfs_work_helper 0.823 0.037
26520.415343 26520.416304 [0031] (w)btrfs_work_helper 0.961
26520.415335 26520.417078 [0001] (w)btrfs_work_helper 1.743
26520.415250 26520.417564 [0002] (w)wb_workfn 2.314
26520.424777 26520.424787 [0002] (w)btrfs_work_helper 0.010
26520.424788 26520.424798 [0002] (w)btrfs_work_helper 0.010
26520.424790 26520.424805 [0001] (w)btrfs_work_helper 0.016 0.016
26520.424801 26520.424807 [0002] (w)btrfs_work_helper 0.006
26520.424809 26520.424831 [0002] (w)btrfs_work_helper 0.022 0.030
26520.424824 26520.424835 [0027] (w)btrfs_work_helper 0.011
26520.424809 26520.424867 [0001] (w)btrfs_work_helper 0.059 0.032
#

Signed-off-by: Yang Jihong <yangjihong1@huawei.com>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Paul Clarke <pc@us.ibm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: https://lore.kernel.org/r/20220709015033.38326-14-yangjihong1@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Yang Jihong and committed by
Arnaldo Carvalho de Melo
bcc8b3e8 53e49e32

+366 -1
+65
tools/perf/Documentation/perf-kwork.txt
··· 21 21 22 22 'perf kwork latency' to report the per kwork latencies. 23 23 24 + 'perf kwork timehist' provides an analysis of kernel work events. 25 + 24 26 Example usage: 25 27 perf kwork record -- sleep 1 26 28 perf kwork report 27 29 perf kwork latency 30 + perf kwork timehist 31 + 32 + By default it shows the individual work events such as irq, workqeueu, 33 + including the run time and delay (time between raise and actually entry): 34 + 35 + Runtime start Runtime end Cpu Kwork name Runtime Delaytime 36 + (TYPE)NAME:NUM (msec) (msec) 37 + ----------------- ----------------- ------ ------------------------- ---------- ---------- 38 + 1811186.976062 1811186.976327 [0000] (s)RCU:9 0.266 0.114 39 + 1811186.978452 1811186.978547 [0000] (s)SCHED:7 0.095 0.171 40 + 1811186.980327 1811186.980490 [0000] (s)SCHED:7 0.162 0.083 41 + 1811186.981221 1811186.981271 [0000] (s)SCHED:7 0.050 0.077 42 + 1811186.984267 1811186.984318 [0000] (s)SCHED:7 0.051 0.075 43 + 1811186.987252 1811186.987315 [0000] (s)SCHED:7 0.063 0.081 44 + 1811186.987785 1811186.987843 [0006] (s)RCU:9 0.058 0.645 45 + 1811186.988319 1811186.988383 [0000] (s)SCHED:7 0.064 0.143 46 + 1811186.989404 1811186.989607 [0002] (s)TIMER:1 0.203 0.111 47 + 1811186.989660 1811186.989732 [0002] (s)SCHED:7 0.072 0.310 48 + 1811186.991295 1811186.991407 [0002] eth0:10 0.112 49 + 1811186.991639 1811186.991734 [0002] (s)NET_RX:3 0.095 0.277 50 + 1811186.989860 1811186.991826 [0002] (w)vmstat_shepherd 1.966 0.345 51 + ... 52 + 53 + Times are in msec.usec. 28 54 29 55 OPTIONS 30 56 ------- ··· 118 92 -s:: 119 93 --sort:: 120 94 Sort by key(s): avg, max, count 95 + 96 + --time:: 97 + Only analyze samples within given time window: <start>,<stop>. Times 98 + have the format seconds.microseconds. If start is not given (i.e., time 99 + string is ',x.y') then analysis starts at the beginning of the file. If 100 + stop time is not given (i.e, time string is 'x.y,') then analysis goes 101 + to end of file. 102 + 103 + OPTIONS for 'perf kwork timehist' 104 + --------------------------------- 105 + 106 + -C:: 107 + --cpu:: 108 + Only show events for the given CPU(s) (comma separated list). 109 + 110 + -g:: 111 + --call-graph:: 112 + Display call chains if present (default off). 113 + 114 + -i:: 115 + --input:: 116 + Input file name. (default: perf.data unless stdin is a fifo) 117 + 118 + -k:: 119 + --vmlinux=<file>:: 120 + Vmlinux pathname 121 + 122 + -n:: 123 + --name:: 124 + Only show events for the given name. 125 + 126 + --kallsyms=<file>:: 127 + Kallsyms pathname 128 + 129 + --max-stack:: 130 + Maximum number of functions to display in backtrace, default 5. 131 + 132 + --symfs=<directory>:: 133 + Look for files with symbols relative to this directory. 121 134 122 135 --time:: 123 136 Only analyze samples within given time window: <start>,<stop>. Times
+298 -1
tools/perf/builtin-kwork.c
··· 35 35 #define PRINT_TIMESTAMP_WIDTH 17 36 36 #define PRINT_KWORK_NAME_WIDTH 30 37 37 #define RPINT_DECIMAL_WIDTH 3 38 + #define PRINT_BRACKETPAIR_WIDTH 2 38 39 #define PRINT_TIME_UNIT_SEC_WIDTH 2 39 40 #define PRINT_TIME_UNIT_MESC_WIDTH 3 40 41 #define PRINT_RUNTIME_HEADER_WIDTH (PRINT_RUNTIME_WIDTH + PRINT_TIME_UNIT_MESC_WIDTH) 41 42 #define PRINT_LATENCY_HEADER_WIDTH (PRINT_LATENCY_WIDTH + PRINT_TIME_UNIT_MESC_WIDTH) 43 + #define PRINT_TIMEHIST_CPU_WIDTH (PRINT_CPU_WIDTH + PRINT_BRACKETPAIR_WIDTH) 42 44 #define PRINT_TIMESTAMP_HEADER_WIDTH (PRINT_TIMESTAMP_WIDTH + PRINT_TIME_UNIT_SEC_WIDTH) 43 45 44 46 struct sort_dimension { ··· 576 574 return 0; 577 575 } 578 576 577 + static void timehist_save_callchain(struct perf_kwork *kwork, 578 + struct perf_sample *sample, 579 + struct evsel *evsel, 580 + struct machine *machine) 581 + { 582 + struct symbol *sym; 583 + struct thread *thread; 584 + struct callchain_cursor_node *node; 585 + struct callchain_cursor *cursor = &callchain_cursor; 586 + 587 + if (!kwork->show_callchain || sample->callchain == NULL) 588 + return; 589 + 590 + /* want main thread for process - has maps */ 591 + thread = machine__findnew_thread(machine, sample->pid, sample->pid); 592 + if (thread == NULL) { 593 + pr_debug("Failed to get thread for pid %d\n", sample->pid); 594 + return; 595 + } 596 + 597 + if (thread__resolve_callchain(thread, cursor, evsel, sample, 598 + NULL, NULL, kwork->max_stack + 2) != 0) { 599 + pr_debug("Failed to resolve callchain, skipping\n"); 600 + goto out_put; 601 + } 602 + 603 + callchain_cursor_commit(cursor); 604 + 605 + while (true) { 606 + node = callchain_cursor_current(cursor); 607 + if (node == NULL) 608 + break; 609 + 610 + sym = node->ms.sym; 611 + if (sym) { 612 + if (!strcmp(sym->name, "__softirqentry_text_start") || 613 + !strcmp(sym->name, "__do_softirq")) 614 + sym->ignore = 1; 615 + } 616 + 617 + callchain_cursor_advance(cursor); 618 + } 619 + 620 + out_put: 621 + thread__put(thread); 622 + } 623 + 624 + static void timehist_print_event(struct perf_kwork *kwork, 625 + struct kwork_work *work, 626 + struct kwork_atom *atom, 627 + struct perf_sample *sample, 628 + struct addr_location *al) 629 + { 630 + char entrytime[32], exittime[32]; 631 + char kwork_name[PRINT_KWORK_NAME_WIDTH]; 632 + 633 + /* 634 + * runtime start 635 + */ 636 + timestamp__scnprintf_usec(atom->time, 637 + entrytime, sizeof(entrytime)); 638 + printf(" %*s ", PRINT_TIMESTAMP_WIDTH, entrytime); 639 + 640 + /* 641 + * runtime end 642 + */ 643 + timestamp__scnprintf_usec(sample->time, 644 + exittime, sizeof(exittime)); 645 + printf(" %*s ", PRINT_TIMESTAMP_WIDTH, exittime); 646 + 647 + /* 648 + * cpu 649 + */ 650 + printf(" [%0*d] ", PRINT_CPU_WIDTH, work->cpu); 651 + 652 + /* 653 + * kwork name 654 + */ 655 + if (work->class && work->class->work_name) { 656 + work->class->work_name(work, kwork_name, 657 + PRINT_KWORK_NAME_WIDTH); 658 + printf(" %-*s ", PRINT_KWORK_NAME_WIDTH, kwork_name); 659 + } else 660 + printf(" %-*s ", PRINT_KWORK_NAME_WIDTH, ""); 661 + 662 + /* 663 + *runtime 664 + */ 665 + printf(" %*.*f ", 666 + PRINT_RUNTIME_WIDTH, RPINT_DECIMAL_WIDTH, 667 + (double)(sample->time - atom->time) / NSEC_PER_MSEC); 668 + 669 + /* 670 + * delaytime 671 + */ 672 + if (atom->prev != NULL) 673 + printf(" %*.*f ", PRINT_LATENCY_WIDTH, RPINT_DECIMAL_WIDTH, 674 + (double)(atom->time - atom->prev->time) / NSEC_PER_MSEC); 675 + else 676 + printf(" %*s ", PRINT_LATENCY_WIDTH, " "); 677 + 678 + /* 679 + * callchain 680 + */ 681 + if (kwork->show_callchain) { 682 + printf(" "); 683 + sample__fprintf_sym(sample, al, 0, 684 + EVSEL__PRINT_SYM | EVSEL__PRINT_ONELINE | 685 + EVSEL__PRINT_CALLCHAIN_ARROW | 686 + EVSEL__PRINT_SKIP_IGNORED, 687 + &callchain_cursor, symbol_conf.bt_stop_list, 688 + stdout); 689 + } 690 + 691 + printf("\n"); 692 + } 693 + 694 + static int timehist_raise_event(struct perf_kwork *kwork, 695 + struct kwork_class *class, 696 + struct evsel *evsel, 697 + struct perf_sample *sample, 698 + struct machine *machine) 699 + { 700 + return work_push_atom(kwork, class, KWORK_TRACE_RAISE, 701 + KWORK_TRACE_MAX, evsel, sample, 702 + machine, NULL); 703 + } 704 + 705 + static int timehist_entry_event(struct perf_kwork *kwork, 706 + struct kwork_class *class, 707 + struct evsel *evsel, 708 + struct perf_sample *sample, 709 + struct machine *machine) 710 + { 711 + int ret; 712 + struct kwork_work *work = NULL; 713 + 714 + ret = work_push_atom(kwork, class, KWORK_TRACE_ENTRY, 715 + KWORK_TRACE_RAISE, evsel, sample, 716 + machine, &work); 717 + if (ret) 718 + return ret; 719 + 720 + if (work != NULL) 721 + timehist_save_callchain(kwork, sample, evsel, machine); 722 + 723 + return 0; 724 + } 725 + 726 + static int timehist_exit_event(struct perf_kwork *kwork, 727 + struct kwork_class *class, 728 + struct evsel *evsel, 729 + struct perf_sample *sample, 730 + struct machine *machine) 731 + { 732 + struct kwork_atom *atom = NULL; 733 + struct kwork_work *work = NULL; 734 + struct addr_location al; 735 + 736 + if (machine__resolve(machine, &al, sample) < 0) { 737 + pr_debug("Problem processing event, skipping it\n"); 738 + return -1; 739 + } 740 + 741 + atom = work_pop_atom(kwork, class, KWORK_TRACE_EXIT, 742 + KWORK_TRACE_ENTRY, evsel, sample, 743 + machine, &work); 744 + if (work == NULL) 745 + return -1; 746 + 747 + if (atom != NULL) { 748 + work->nr_atoms++; 749 + timehist_print_event(kwork, work, atom, sample, &al); 750 + atom_del(atom); 751 + } 752 + 753 + return 0; 754 + } 755 + 579 756 static struct kwork_class kwork_irq; 580 757 static int process_irq_handler_entry_event(struct perf_tool *tool, 581 758 struct evsel *evsel, ··· 1172 991 return ret; 1173 992 } 1174 993 994 + static void timehist_print_header(void) 995 + { 996 + /* 997 + * header row 998 + */ 999 + printf(" %-*s %-*s %-*s %-*s %-*s %-*s\n", 1000 + PRINT_TIMESTAMP_WIDTH, "Runtime start", 1001 + PRINT_TIMESTAMP_WIDTH, "Runtime end", 1002 + PRINT_TIMEHIST_CPU_WIDTH, "Cpu", 1003 + PRINT_KWORK_NAME_WIDTH, "Kwork name", 1004 + PRINT_RUNTIME_WIDTH, "Runtime", 1005 + PRINT_RUNTIME_WIDTH, "Delaytime"); 1006 + 1007 + /* 1008 + * units row 1009 + */ 1010 + printf(" %-*s %-*s %-*s %-*s %-*s %-*s\n", 1011 + PRINT_TIMESTAMP_WIDTH, "", 1012 + PRINT_TIMESTAMP_WIDTH, "", 1013 + PRINT_TIMEHIST_CPU_WIDTH, "", 1014 + PRINT_KWORK_NAME_WIDTH, "(TYPE)NAME:NUM", 1015 + PRINT_RUNTIME_WIDTH, "(msec)", 1016 + PRINT_RUNTIME_WIDTH, "(msec)"); 1017 + 1018 + /* 1019 + * separator 1020 + */ 1021 + printf(" %.*s %.*s %.*s %.*s %.*s %.*s\n", 1022 + PRINT_TIMESTAMP_WIDTH, graph_dotted_line, 1023 + PRINT_TIMESTAMP_WIDTH, graph_dotted_line, 1024 + PRINT_TIMEHIST_CPU_WIDTH, graph_dotted_line, 1025 + PRINT_KWORK_NAME_WIDTH, graph_dotted_line, 1026 + PRINT_RUNTIME_WIDTH, graph_dotted_line, 1027 + PRINT_RUNTIME_WIDTH, graph_dotted_line); 1028 + } 1029 + 1175 1030 static void print_summary(struct perf_kwork *kwork) 1176 1031 { 1177 1032 u64 time = kwork->timeend - kwork->timestart; ··· 1302 1085 struct perf_session *session) 1303 1086 { 1304 1087 int ret; 1088 + struct evsel *evsel; 1305 1089 struct kwork_class *class; 1306 1090 1307 1091 static struct trace_kwork_handler report_ops = { ··· 1313 1095 .raise_event = latency_raise_event, 1314 1096 .entry_event = latency_entry_event, 1315 1097 }; 1098 + static struct trace_kwork_handler timehist_ops = { 1099 + .raise_event = timehist_raise_event, 1100 + .entry_event = timehist_entry_event, 1101 + .exit_event = timehist_exit_event, 1102 + }; 1316 1103 1317 1104 switch (kwork->report) { 1318 1105 case KWORK_REPORT_RUNTIME: ··· 1325 1102 break; 1326 1103 case KWORK_REPORT_LATENCY: 1327 1104 kwork->tp_handler = &latency_ops; 1105 + break; 1106 + case KWORK_REPORT_TIMEHIST: 1107 + kwork->tp_handler = &timehist_ops; 1328 1108 break; 1329 1109 default: 1330 1110 pr_debug("Invalid report type %d\n", kwork->report); ··· 1354 1128 if (ret != 0) { 1355 1129 pr_err("Invalid time span\n"); 1356 1130 return -1; 1131 + } 1132 + } 1133 + 1134 + list_for_each_entry(evsel, &session->evlist->core.entries, core.node) { 1135 + if (kwork->show_callchain && !evsel__has_callchain(evsel)) { 1136 + pr_debug("Samples do not have callchains\n"); 1137 + kwork->show_callchain = 0; 1138 + symbol_conf.use_callchain = 0; 1357 1139 } 1358 1140 } 1359 1141 ··· 1397 1163 pr_err("Failed to set libtraceevent function resolver\n"); 1398 1164 goto out_delete; 1399 1165 } 1166 + 1167 + if (kwork->report == KWORK_REPORT_TIMEHIST) 1168 + timehist_print_header(); 1400 1169 1401 1170 ret = perf_session__process_events(session); 1402 1171 if (ret) { ··· 1492 1255 } 1493 1256 1494 1257 return err; 1258 + } 1259 + 1260 + static int perf_kwork__timehist(struct perf_kwork *kwork) 1261 + { 1262 + /* 1263 + * event handlers for timehist option 1264 + */ 1265 + kwork->tool.comm = perf_event__process_comm; 1266 + kwork->tool.exit = perf_event__process_exit; 1267 + kwork->tool.fork = perf_event__process_fork; 1268 + kwork->tool.attr = perf_event__process_attr; 1269 + kwork->tool.tracing_data = perf_event__process_tracing_data; 1270 + kwork->tool.build_id = perf_event__process_build_id; 1271 + kwork->tool.ordered_events = true; 1272 + kwork->tool.ordering_requires_timestamps = true; 1273 + symbol_conf.use_callchain = kwork->show_callchain; 1274 + 1275 + if (symbol__validate_sym_arguments()) { 1276 + pr_err("Failed to validate sym arguments\n"); 1277 + return -1; 1278 + } 1279 + 1280 + setup_pager(); 1281 + 1282 + return perf_kwork__read_events(kwork); 1495 1283 } 1496 1284 1497 1285 static void setup_event_list(struct perf_kwork *kwork, ··· 1632 1370 .event_list_str = NULL, 1633 1371 .summary = false, 1634 1372 .sort_order = NULL, 1373 + .show_callchain = false, 1374 + .max_stack = 5, 1635 1375 .timestart = 0, 1636 1376 .timeend = 0, 1637 1377 .nr_events = 0, ··· 1683 1419 "input file name"), 1684 1420 OPT_PARENT(kwork_options) 1685 1421 }; 1422 + const struct option timehist_options[] = { 1423 + OPT_STRING('k', "vmlinux", &symbol_conf.vmlinux_name, 1424 + "file", "vmlinux pathname"), 1425 + OPT_STRING(0, "kallsyms", &symbol_conf.kallsyms_name, 1426 + "file", "kallsyms pathname"), 1427 + OPT_BOOLEAN('g', "call-graph", &kwork.show_callchain, 1428 + "Display call chains if present"), 1429 + OPT_UINTEGER(0, "max-stack", &kwork.max_stack, 1430 + "Maximum number of functions to display backtrace."), 1431 + OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory", 1432 + "Look for files with symbols relative to this directory"), 1433 + OPT_STRING(0, "time", &kwork.time_str, "str", 1434 + "Time span for analysis (start,stop)"), 1435 + OPT_STRING('C', "cpu", &kwork.cpu_list, "cpu", 1436 + "list of cpus to profile"), 1437 + OPT_STRING('n', "name", &kwork.profile_name, "name", 1438 + "event name to profile"), 1439 + OPT_STRING('i', "input", &input_name, "file", 1440 + "input file name"), 1441 + OPT_PARENT(kwork_options) 1442 + }; 1686 1443 const char *kwork_usage[] = { 1687 1444 NULL, 1688 1445 NULL ··· 1716 1431 "perf kwork latency [<options>]", 1717 1432 NULL 1718 1433 }; 1434 + const char * const timehist_usage[] = { 1435 + "perf kwork timehist [<options>]", 1436 + NULL 1437 + }; 1719 1438 const char *const kwork_subcommands[] = { 1720 - "record", "report", "latency", NULL 1439 + "record", "report", "latency", "timehist", NULL 1721 1440 }; 1722 1441 1723 1442 argc = parse_options_subcommand(argc, argv, kwork_options, ··· 1755 1466 kwork.report = KWORK_REPORT_LATENCY; 1756 1467 setup_sorting(&kwork, latency_options, latency_usage); 1757 1468 return perf_kwork__report(&kwork); 1469 + } else if (strlen(argv[0]) > 2 && strstarts("timehist", argv[0])) { 1470 + if (argc > 1) { 1471 + argc = parse_options(argc, argv, timehist_options, timehist_usage, 0); 1472 + if (argc) 1473 + usage_with_options(timehist_usage, timehist_options); 1474 + } 1475 + kwork.report = KWORK_REPORT_TIMEHIST; 1476 + return perf_kwork__timehist(&kwork); 1758 1477 } else 1759 1478 usage_with_options(kwork_usage, kwork_options); 1760 1479
+3
tools/perf/util/kwork.h
··· 22 22 enum kwork_report_type { 23 23 KWORK_REPORT_RUNTIME, 24 24 KWORK_REPORT_LATENCY, 25 + KWORK_REPORT_TIMEHIST, 25 26 }; 26 27 27 28 enum kwork_trace_type { ··· 201 200 */ 202 201 bool summary; 203 202 const char *sort_order; 203 + bool show_callchain; 204 + unsigned int max_stack; 204 205 205 206 /* 206 207 * statistics