diff options
Diffstat (limited to 'tools')
| -rw-r--r-- | tools/perf/Documentation/perf-timechart.txt | 38 | ||||
| -rw-r--r-- | tools/perf/builtin-timechart.c | 693 | ||||
| -rw-r--r-- | tools/perf/util/svghelper.c | 168 | ||||
| -rw-r--r-- | tools/perf/util/svghelper.h | 6 |
4 files changed, 845 insertions, 60 deletions
diff --git a/tools/perf/Documentation/perf-timechart.txt b/tools/perf/Documentation/perf-timechart.txt index 5e0f986dff38..df98d1c82688 100644 --- a/tools/perf/Documentation/perf-timechart.txt +++ b/tools/perf/Documentation/perf-timechart.txt | |||
| @@ -15,10 +15,20 @@ DESCRIPTION | |||
| 15 | There are two variants of perf timechart: | 15 | There are two variants of perf timechart: |
| 16 | 16 | ||
| 17 | 'perf timechart record <command>' to record the system level events | 17 | 'perf timechart record <command>' to record the system level events |
| 18 | of an arbitrary workload. | 18 | of an arbitrary workload. By default timechart records only scheduler |
| 19 | and CPU events (task switches, running times, CPU power states, etc), | ||
| 20 | but it's possible to record IO (disk, network) activity using -I argument. | ||
| 19 | 21 | ||
| 20 | 'perf timechart' to turn a trace into a Scalable Vector Graphics file, | 22 | 'perf timechart' to turn a trace into a Scalable Vector Graphics file, |
| 21 | that can be viewed with popular SVG viewers such as 'Inkscape'. | 23 | that can be viewed with popular SVG viewers such as 'Inkscape'. Depending |
| 24 | on the events in the perf.data file, timechart will contain scheduler/cpu | ||
| 25 | events or IO events. | ||
| 26 | |||
| 27 | In IO mode, every bar has two charts: upper and lower. | ||
| 28 | Upper bar shows incoming events (disk reads, ingress network packets). | ||
| 29 | Lower bar shows outgoing events (disk writes, egress network packets). | ||
| 30 | There are also poll bars which show how much time application spent | ||
| 31 | in poll/epoll/select syscalls. | ||
| 22 | 32 | ||
| 23 | TIMECHART OPTIONS | 33 | TIMECHART OPTIONS |
| 24 | ----------------- | 34 | ----------------- |
| @@ -54,6 +64,19 @@ TIMECHART OPTIONS | |||
| 54 | duration or tasks with given name. If number is given it's interpreted | 64 | duration or tasks with given name. If number is given it's interpreted |
| 55 | as number of nanoseconds. If non-numeric string is given it's | 65 | as number of nanoseconds. If non-numeric string is given it's |
| 56 | interpreted as task name. | 66 | interpreted as task name. |
| 67 | --io-skip-eagain:: | ||
| 68 | Don't draw EAGAIN IO events. | ||
| 69 | --io-min-time=<nsecs>:: | ||
| 70 | Draw small events as if they lasted min-time. Useful when you need | ||
| 71 | to see very small and fast IO. It's possible to specify ms or us | ||
| 72 | suffix to specify time in milliseconds or microseconds. | ||
| 73 | Default value is 1ms. | ||
| 74 | --io-merge-dist=<nsecs>:: | ||
| 75 | Merge events that are merge-dist nanoseconds apart. | ||
| 76 | Reduces number of figures on the SVG and makes it more render-friendly. | ||
| 77 | It's possible to specify ms or us suffix to specify time in | ||
| 78 | milliseconds or microseconds. | ||
| 79 | Default value is 1us. | ||
| 57 | 80 | ||
| 58 | RECORD OPTIONS | 81 | RECORD OPTIONS |
| 59 | -------------- | 82 | -------------- |
| @@ -63,6 +86,9 @@ RECORD OPTIONS | |||
| 63 | -T:: | 86 | -T:: |
| 64 | --tasks-only:: | 87 | --tasks-only:: |
| 65 | Record only tasks-related events | 88 | Record only tasks-related events |
| 89 | -I:: | ||
| 90 | --io-only:: | ||
| 91 | Record only io-related events | ||
| 66 | -g:: | 92 | -g:: |
| 67 | --callchain:: | 93 | --callchain:: |
| 68 | Do call-graph (stack chain/backtrace) recording | 94 | Do call-graph (stack chain/backtrace) recording |
| @@ -87,6 +113,14 @@ Record system-wide timechart: | |||
| 87 | 113 | ||
| 88 | $ perf timechart --highlight gcc | 114 | $ perf timechart --highlight gcc |
| 89 | 115 | ||
| 116 | Record system-wide IO events: | ||
| 117 | |||
| 118 | $ perf timechart record -I | ||
| 119 | |||
| 120 | then generate timechart: | ||
| 121 | |||
| 122 | $ perf timechart | ||
| 123 | |||
| 90 | SEE ALSO | 124 | SEE ALSO |
| 91 | -------- | 125 | -------- |
| 92 | linkperf:perf-record[1] | 126 | linkperf:perf-record[1] |
diff --git a/tools/perf/builtin-timechart.c b/tools/perf/builtin-timechart.c index 74db2568b867..04c9c53becad 100644 --- a/tools/perf/builtin-timechart.c +++ b/tools/perf/builtin-timechart.c | |||
| @@ -60,10 +60,17 @@ struct timechart { | |||
| 60 | tasks_only, | 60 | tasks_only, |
| 61 | with_backtrace, | 61 | with_backtrace, |
| 62 | topology; | 62 | topology; |
| 63 | /* IO related settings */ | ||
| 64 | u64 io_events; | ||
| 65 | bool io_only, | ||
| 66 | skip_eagain; | ||
| 67 | u64 min_time, | ||
| 68 | merge_dist; | ||
| 63 | }; | 69 | }; |
| 64 | 70 | ||
| 65 | struct per_pidcomm; | 71 | struct per_pidcomm; |
| 66 | struct cpu_sample; | 72 | struct cpu_sample; |
| 73 | struct io_sample; | ||
| 67 | 74 | ||
| 68 | /* | 75 | /* |
| 69 | * Datastructure layout: | 76 | * Datastructure layout: |
| @@ -84,6 +91,7 @@ struct per_pid { | |||
| 84 | u64 start_time; | 91 | u64 start_time; |
| 85 | u64 end_time; | 92 | u64 end_time; |
| 86 | u64 total_time; | 93 | u64 total_time; |
| 94 | u64 total_bytes; | ||
| 87 | int display; | 95 | int display; |
| 88 | 96 | ||
| 89 | struct per_pidcomm *all; | 97 | struct per_pidcomm *all; |
| @@ -97,6 +105,8 @@ struct per_pidcomm { | |||
| 97 | u64 start_time; | 105 | u64 start_time; |
| 98 | u64 end_time; | 106 | u64 end_time; |
| 99 | u64 total_time; | 107 | u64 total_time; |
| 108 | u64 max_bytes; | ||
| 109 | u64 total_bytes; | ||
| 100 | 110 | ||
| 101 | int Y; | 111 | int Y; |
| 102 | int display; | 112 | int display; |
| @@ -107,6 +117,7 @@ struct per_pidcomm { | |||
| 107 | char *comm; | 117 | char *comm; |
| 108 | 118 | ||
| 109 | struct cpu_sample *samples; | 119 | struct cpu_sample *samples; |
| 120 | struct io_sample *io_samples; | ||
| 110 | }; | 121 | }; |
| 111 | 122 | ||
| 112 | struct sample_wrapper { | 123 | struct sample_wrapper { |
| @@ -131,6 +142,27 @@ struct cpu_sample { | |||
| 131 | const char *backtrace; | 142 | const char *backtrace; |
| 132 | }; | 143 | }; |
| 133 | 144 | ||
| 145 | enum { | ||
| 146 | IOTYPE_READ, | ||
| 147 | IOTYPE_WRITE, | ||
| 148 | IOTYPE_SYNC, | ||
| 149 | IOTYPE_TX, | ||
| 150 | IOTYPE_RX, | ||
| 151 | IOTYPE_POLL, | ||
| 152 | }; | ||
| 153 | |||
| 154 | struct io_sample { | ||
| 155 | struct io_sample *next; | ||
| 156 | |||
| 157 | u64 start_time; | ||
| 158 | u64 end_time; | ||
| 159 | u64 bytes; | ||
| 160 | int type; | ||
| 161 | int fd; | ||
| 162 | int err; | ||
| 163 | int merges; | ||
| 164 | }; | ||
| 165 | |||
| 134 | #define CSTATE 1 | 166 | #define CSTATE 1 |
| 135 | #define PSTATE 2 | 167 | #define PSTATE 2 |
| 136 | 168 | ||
| @@ -213,7 +245,7 @@ static void pid_fork(struct timechart *tchart, int pid, int ppid, u64 timestamp) | |||
| 213 | pid_set_comm(tchart, pid, pp->current->comm); | 245 | pid_set_comm(tchart, pid, pp->current->comm); |
| 214 | 246 | ||
| 215 | p->start_time = timestamp; | 247 | p->start_time = timestamp; |
| 216 | if (p->current) { | 248 | if (p->current && !p->current->start_time) { |
| 217 | p->current->start_time = timestamp; | 249 | p->current->start_time = timestamp; |
| 218 | p->current->state_since = timestamp; | 250 | p->current->state_since = timestamp; |
| 219 | } | 251 | } |
| @@ -682,6 +714,249 @@ static void end_sample_processing(struct timechart *tchart) | |||
| 682 | } | 714 | } |
| 683 | } | 715 | } |
| 684 | 716 | ||
| 717 | static int pid_begin_io_sample(struct timechart *tchart, int pid, int type, | ||
| 718 | u64 start, int fd) | ||
| 719 | { | ||
| 720 | struct per_pid *p = find_create_pid(tchart, pid); | ||
| 721 | struct per_pidcomm *c = p->current; | ||
| 722 | struct io_sample *sample; | ||
| 723 | struct io_sample *prev; | ||
| 724 | |||
| 725 | if (!c) { | ||
| 726 | c = zalloc(sizeof(*c)); | ||
| 727 | if (!c) | ||
| 728 | return -ENOMEM; | ||
| 729 | p->current = c; | ||
| 730 | c->next = p->all; | ||
| 731 | p->all = c; | ||
| 732 | } | ||
| 733 | |||
| 734 | prev = c->io_samples; | ||
| 735 | |||
| 736 | if (prev && prev->start_time && !prev->end_time) { | ||
| 737 | pr_warning("Skip invalid start event: " | ||
| 738 | "previous event already started!\n"); | ||
| 739 | |||
| 740 | /* remove previous event that has been started, | ||
| 741 | * we are not sure we will ever get an end for it */ | ||
| 742 | c->io_samples = prev->next; | ||
| 743 | free(prev); | ||
| 744 | return 0; | ||
| 745 | } | ||
| 746 | |||
| 747 | sample = zalloc(sizeof(*sample)); | ||
| 748 | if (!sample) | ||
| 749 | return -ENOMEM; | ||
| 750 | sample->start_time = start; | ||
| 751 | sample->type = type; | ||
| 752 | sample->fd = fd; | ||
| 753 | sample->next = c->io_samples; | ||
| 754 | c->io_samples = sample; | ||
| 755 | |||
| 756 | if (c->start_time == 0 || c->start_time > start) | ||
| 757 | c->start_time = start; | ||
| 758 | |||
| 759 | return 0; | ||
| 760 | } | ||
| 761 | |||
| 762 | static int pid_end_io_sample(struct timechart *tchart, int pid, int type, | ||
| 763 | u64 end, long ret) | ||
| 764 | { | ||
| 765 | struct per_pid *p = find_create_pid(tchart, pid); | ||
| 766 | struct per_pidcomm *c = p->current; | ||
| 767 | struct io_sample *sample, *prev; | ||
| 768 | |||
| 769 | if (!c) { | ||
| 770 | pr_warning("Invalid pidcomm!\n"); | ||
| 771 | return -1; | ||
| 772 | } | ||
| 773 | |||
| 774 | sample = c->io_samples; | ||
| 775 | |||
| 776 | if (!sample) /* skip partially captured events */ | ||
| 777 | return 0; | ||
| 778 | |||
| 779 | if (sample->end_time) { | ||
| 780 | pr_warning("Skip invalid end event: " | ||
| 781 | "previous event already ended!\n"); | ||
| 782 | return 0; | ||
| 783 | } | ||
| 784 | |||
| 785 | if (sample->type != type) { | ||
| 786 | pr_warning("Skip invalid end event: invalid event type!\n"); | ||
| 787 | return 0; | ||
| 788 | } | ||
| 789 | |||
| 790 | sample->end_time = end; | ||
| 791 | prev = sample->next; | ||
| 792 | |||
| 793 | /* we want to be able to see small and fast transfers, so make them | ||
| 794 | * at least min_time long, but don't overlap them */ | ||
| 795 | if (sample->end_time - sample->start_time < tchart->min_time) | ||
| 796 | sample->end_time = sample->start_time + tchart->min_time; | ||
| 797 | if (prev && sample->start_time < prev->end_time) { | ||
| 798 | if (prev->err) /* try to make errors more visible */ | ||
| 799 | sample->start_time = prev->end_time; | ||
| 800 | else | ||
| 801 | prev->end_time = sample->start_time; | ||
| 802 | } | ||
| 803 | |||
| 804 | if (ret < 0) { | ||
| 805 | sample->err = ret; | ||
| 806 | } else if (type == IOTYPE_READ || type == IOTYPE_WRITE || | ||
| 807 | type == IOTYPE_TX || type == IOTYPE_RX) { | ||
| 808 | |||
| 809 | if ((u64)ret > c->max_bytes) | ||
| 810 | c->max_bytes = ret; | ||
| 811 | |||
| 812 | c->total_bytes += ret; | ||
| 813 | p->total_bytes += ret; | ||
| 814 | sample->bytes = ret; | ||
| 815 | } | ||
| 816 | |||
| 817 | /* merge two requests to make svg smaller and render-friendly */ | ||
| 818 | if (prev && | ||
| 819 | prev->type == sample->type && | ||
| 820 | prev->err == sample->err && | ||
| 821 | prev->fd == sample->fd && | ||
| 822 | prev->end_time + tchart->merge_dist >= sample->start_time) { | ||
| 823 | |||
| 824 | sample->bytes += prev->bytes; | ||
| 825 | sample->merges += prev->merges + 1; | ||
| 826 | |||
| 827 | sample->start_time = prev->start_time; | ||
| 828 | sample->next = prev->next; | ||
| 829 | free(prev); | ||
| 830 | |||
| 831 | if (!sample->err && sample->bytes > c->max_bytes) | ||
| 832 | c->max_bytes = sample->bytes; | ||
| 833 | } | ||
| 834 | |||
| 835 | tchart->io_events++; | ||
| 836 | |||
| 837 | return 0; | ||
| 838 | } | ||
| 839 | |||
| 840 | static int | ||
| 841 | process_enter_read(struct timechart *tchart, | ||
| 842 | struct perf_evsel *evsel, | ||
| 843 | struct perf_sample *sample) | ||
| 844 | { | ||
| 845 | long fd = perf_evsel__intval(evsel, sample, "fd"); | ||
| 846 | return pid_begin_io_sample(tchart, sample->tid, IOTYPE_READ, | ||
| 847 | sample->time, fd); | ||
| 848 | } | ||
| 849 | |||
| 850 | static int | ||
| 851 | process_exit_read(struct timechart *tchart, | ||
| 852 | struct perf_evsel *evsel, | ||
| 853 | struct perf_sample *sample) | ||
| 854 | { | ||
| 855 | long ret = perf_evsel__intval(evsel, sample, "ret"); | ||
| 856 | return pid_end_io_sample(tchart, sample->tid, IOTYPE_READ, | ||
| 857 | sample->time, ret); | ||
| 858 | } | ||
| 859 | |||
| 860 | static int | ||
| 861 | process_enter_write(struct timechart *tchart, | ||
| 862 | struct perf_evsel *evsel, | ||
| 863 | struct perf_sample *sample) | ||
| 864 | { | ||
| 865 | long fd = perf_evsel__intval(evsel, sample, "fd"); | ||
| 866 | return pid_begin_io_sample(tchart, sample->tid, IOTYPE_WRITE, | ||
| 867 | sample->time, fd); | ||
| 868 | } | ||
| 869 | |||
| 870 | static int | ||
| 871 | process_exit_write(struct timechart *tchart, | ||
| 872 | struct perf_evsel *evsel, | ||
| 873 | struct perf_sample *sample) | ||
| 874 | { | ||
| 875 | long ret = perf_evsel__intval(evsel, sample, "ret"); | ||
| 876 | return pid_end_io_sample(tchart, sample->tid, IOTYPE_WRITE, | ||
| 877 | sample->time, ret); | ||
| 878 | } | ||
| 879 | |||
| 880 | static int | ||
| 881 | process_enter_sync(struct timechart *tchart, | ||
| 882 | struct perf_evsel *evsel, | ||
| 883 | struct perf_sample *sample) | ||
| 884 | { | ||
| 885 | long fd = perf_evsel__intval(evsel, sample, "fd"); | ||
| 886 | return pid_begin_io_sample(tchart, sample->tid, IOTYPE_SYNC, | ||
| 887 | sample->time, fd); | ||
| 888 | } | ||
| 889 | |||
| 890 | static int | ||
| 891 | process_exit_sync(struct timechart *tchart, | ||
| 892 | struct perf_evsel *evsel, | ||
| 893 | struct perf_sample *sample) | ||
| 894 | { | ||
| 895 | long ret = perf_evsel__intval(evsel, sample, "ret"); | ||
| 896 | return pid_end_io_sample(tchart, sample->tid, IOTYPE_SYNC, | ||
| 897 | sample->time, ret); | ||
| 898 | } | ||
| 899 | |||
| 900 | static int | ||
| 901 | process_enter_tx(struct timechart *tchart, | ||
| 902 | struct perf_evsel *evsel, | ||
| 903 | struct perf_sample *sample) | ||
| 904 | { | ||
| 905 | long fd = perf_evsel__intval(evsel, sample, "fd"); | ||
| 906 | return pid_begin_io_sample(tchart, sample->tid, IOTYPE_TX, | ||
| 907 | sample->time, fd); | ||
| 908 | } | ||
| 909 | |||
| 910 | static int | ||
| 911 | process_exit_tx(struct timechart *tchart, | ||
| 912 | struct perf_evsel *evsel, | ||
| 913 | struct perf_sample *sample) | ||
| 914 | { | ||
| 915 | long ret = perf_evsel__intval(evsel, sample, "ret"); | ||
| 916 | return pid_end_io_sample(tchart, sample->tid, IOTYPE_TX, | ||
| 917 | sample->time, ret); | ||
| 918 | } | ||
| 919 | |||
| 920 | static int | ||
| 921 | process_enter_rx(struct timechart *tchart, | ||
| 922 | struct perf_evsel *evsel, | ||
| 923 | struct perf_sample *sample) | ||
| 924 | { | ||
| 925 | long fd = perf_evsel__intval(evsel, sample, "fd"); | ||
| 926 | return pid_begin_io_sample(tchart, sample->tid, IOTYPE_RX, | ||
| 927 | sample->time, fd); | ||
| 928 | } | ||
| 929 | |||
| 930 | static int | ||
| 931 | process_exit_rx(struct timechart *tchart, | ||
| 932 | struct perf_evsel *evsel, | ||
| 933 | struct perf_sample *sample) | ||
| 934 | { | ||
| 935 | long ret = perf_evsel__intval(evsel, sample, "ret"); | ||
| 936 | return pid_end_io_sample(tchart, sample->tid, IOTYPE_RX, | ||
| 937 | sample->time, ret); | ||
| 938 | } | ||
| 939 | |||
| 940 | static int | ||
| 941 | process_enter_poll(struct timechart *tchart, | ||
| 942 | struct perf_evsel *evsel, | ||
| 943 | struct perf_sample *sample) | ||
| 944 | { | ||
| 945 | long fd = perf_evsel__intval(evsel, sample, "fd"); | ||
| 946 | return pid_begin_io_sample(tchart, sample->tid, IOTYPE_POLL, | ||
| 947 | sample->time, fd); | ||
| 948 | } | ||
| 949 | |||
| 950 | static int | ||
| 951 | process_exit_poll(struct timechart *tchart, | ||
| 952 | struct perf_evsel *evsel, | ||
| 953 | struct perf_sample *sample) | ||
| 954 | { | ||
| 955 | long ret = perf_evsel__intval(evsel, sample, "ret"); | ||
| 956 | return pid_end_io_sample(tchart, sample->tid, IOTYPE_POLL, | ||
| 957 | sample->time, ret); | ||
| 958 | } | ||
| 959 | |||
| 685 | /* | 960 | /* |
| 686 | * Sort the pid datastructure | 961 | * Sort the pid datastructure |
| 687 | */ | 962 | */ |
| @@ -852,6 +1127,121 @@ static void draw_cpu_usage(struct timechart *tchart) | |||
| 852 | } | 1127 | } |
| 853 | } | 1128 | } |
| 854 | 1129 | ||
| 1130 | static void draw_io_bars(struct timechart *tchart) | ||
| 1131 | { | ||
| 1132 | const char *suf; | ||
| 1133 | double bytes; | ||
| 1134 | char comm[256]; | ||
| 1135 | struct per_pid *p; | ||
| 1136 | struct per_pidcomm *c; | ||
| 1137 | struct io_sample *sample; | ||
| 1138 | int Y = 1; | ||
| 1139 | |||
| 1140 | p = tchart->all_data; | ||
| 1141 | while (p) { | ||
| 1142 | c = p->all; | ||
| 1143 | while (c) { | ||
| 1144 | if (!c->display) { | ||
| 1145 | c->Y = 0; | ||
| 1146 | c = c->next; | ||
| 1147 | continue; | ||
| 1148 | } | ||
| 1149 | |||
| 1150 | svg_box(Y, c->start_time, c->end_time, "process3"); | ||
| 1151 | sample = c->io_samples; | ||
| 1152 | for (sample = c->io_samples; sample; sample = sample->next) { | ||
| 1153 | double h = (double)sample->bytes / c->max_bytes; | ||
| 1154 | |||
| 1155 | if (tchart->skip_eagain && | ||
| 1156 | sample->err == -EAGAIN) | ||
| 1157 | continue; | ||
| 1158 | |||
| 1159 | if (sample->err) | ||
| 1160 | h = 1; | ||
| 1161 | |||
| 1162 | if (sample->type == IOTYPE_SYNC) | ||
| 1163 | svg_fbox(Y, | ||
| 1164 | sample->start_time, | ||
| 1165 | sample->end_time, | ||
| 1166 | 1, | ||
| 1167 | sample->err ? "error" : "sync", | ||
| 1168 | sample->fd, | ||
| 1169 | sample->err, | ||
| 1170 | sample->merges); | ||
| 1171 | else if (sample->type == IOTYPE_POLL) | ||
| 1172 | svg_fbox(Y, | ||
| 1173 | sample->start_time, | ||
| 1174 | sample->end_time, | ||
| 1175 | 1, | ||
| 1176 | sample->err ? "error" : "poll", | ||
| 1177 | sample->fd, | ||
| 1178 | sample->err, | ||
| 1179 | sample->merges); | ||
| 1180 | else if (sample->type == IOTYPE_READ) | ||
| 1181 | svg_ubox(Y, | ||
| 1182 | sample->start_time, | ||
| 1183 | sample->end_time, | ||
| 1184 | h, | ||
| 1185 | sample->err ? "error" : "disk", | ||
| 1186 | sample->fd, | ||
| 1187 | sample->err, | ||
| 1188 | sample->merges); | ||
| 1189 | else if (sample->type == IOTYPE_WRITE) | ||
| 1190 | svg_lbox(Y, | ||
| 1191 | sample->start_time, | ||
| 1192 | sample->end_time, | ||
| 1193 | h, | ||
| 1194 | sample->err ? "error" : "disk", | ||
| 1195 | sample->fd, | ||
| 1196 | sample->err, | ||
| 1197 | sample->merges); | ||
| 1198 | else if (sample->type == IOTYPE_RX) | ||
| 1199 | svg_ubox(Y, | ||
| 1200 | sample->start_time, | ||
| 1201 | sample->end_time, | ||
| 1202 | h, | ||
| 1203 | sample->err ? "error" : "net", | ||
| 1204 | sample->fd, | ||
| 1205 | sample->err, | ||
| 1206 | sample->merges); | ||
| 1207 | else if (sample->type == IOTYPE_TX) | ||
| 1208 | svg_lbox(Y, | ||
| 1209 | sample->start_time, | ||
| 1210 | sample->end_time, | ||
| 1211 | h, | ||
| 1212 | sample->err ? "error" : "net", | ||
| 1213 | sample->fd, | ||
| 1214 | sample->err, | ||
| 1215 | sample->merges); | ||
| 1216 | } | ||
| 1217 | |||
| 1218 | suf = ""; | ||
| 1219 | bytes = c->total_bytes; | ||
| 1220 | if (bytes > 1024) { | ||
| 1221 | bytes = bytes / 1024; | ||
| 1222 | suf = "K"; | ||
| 1223 | } | ||
| 1224 | if (bytes > 1024) { | ||
| 1225 | bytes = bytes / 1024; | ||
| 1226 | suf = "M"; | ||
| 1227 | } | ||
| 1228 | if (bytes > 1024) { | ||
| 1229 | bytes = bytes / 1024; | ||
| 1230 | suf = "G"; | ||
| 1231 | } | ||
| 1232 | |||
| 1233 | |||
| 1234 | sprintf(comm, "%s:%i (%3.1f %sbytes)", c->comm ?: "", p->pid, bytes, suf); | ||
| 1235 | svg_text(Y, c->start_time, comm); | ||
| 1236 | |||
| 1237 | c->Y = Y; | ||
| 1238 | Y++; | ||
| 1239 | c = c->next; | ||
| 1240 | } | ||
| 1241 | p = p->next; | ||
| 1242 | } | ||
| 1243 | } | ||
| 1244 | |||
| 855 | static void draw_process_bars(struct timechart *tchart) | 1245 | static void draw_process_bars(struct timechart *tchart) |
| 856 | { | 1246 | { |
| 857 | struct per_pid *p; | 1247 | struct per_pid *p; |
| @@ -987,9 +1377,6 @@ static int determine_display_tasks(struct timechart *tchart, u64 threshold) | |||
| 987 | struct per_pidcomm *c; | 1377 | struct per_pidcomm *c; |
| 988 | int count = 0; | 1378 | int count = 0; |
| 989 | 1379 | ||
| 990 | if (process_filter) | ||
| 991 | return determine_display_tasks_filtered(tchart); | ||
| 992 | |||
| 993 | p = tchart->all_data; | 1380 | p = tchart->all_data; |
| 994 | while (p) { | 1381 | while (p) { |
| 995 | p->display = 0; | 1382 | p->display = 0; |
| @@ -1025,15 +1412,46 @@ static int determine_display_tasks(struct timechart *tchart, u64 threshold) | |||
| 1025 | return count; | 1412 | return count; |
| 1026 | } | 1413 | } |
| 1027 | 1414 | ||
| 1415 | static int determine_display_io_tasks(struct timechart *timechart, u64 threshold) | ||
| 1416 | { | ||
| 1417 | struct per_pid *p; | ||
| 1418 | struct per_pidcomm *c; | ||
| 1419 | int count = 0; | ||
| 1420 | |||
| 1421 | p = timechart->all_data; | ||
| 1422 | while (p) { | ||
| 1423 | /* no exit marker, task kept running to the end */ | ||
| 1424 | if (p->end_time == 0) | ||
| 1425 | p->end_time = timechart->last_time; | ||
| 1028 | 1426 | ||
| 1427 | c = p->all; | ||
| 1029 | 1428 | ||
| 1429 | while (c) { | ||
| 1430 | c->display = 0; | ||
| 1431 | |||
| 1432 | if (c->total_bytes >= threshold) { | ||
| 1433 | c->display = 1; | ||
| 1434 | count++; | ||
| 1435 | } | ||
| 1436 | |||
| 1437 | if (c->end_time == 0) | ||
| 1438 | c->end_time = timechart->last_time; | ||
| 1439 | |||
| 1440 | c = c->next; | ||
| 1441 | } | ||
| 1442 | p = p->next; | ||
| 1443 | } | ||
| 1444 | return count; | ||
| 1445 | } | ||
| 1446 | |||
| 1447 | #define BYTES_THRESH (1 * 1024 * 1024) | ||
| 1030 | #define TIME_THRESH 10000000 | 1448 | #define TIME_THRESH 10000000 |
| 1031 | 1449 | ||
| 1032 | static void write_svg_file(struct timechart *tchart, const char *filename) | 1450 | static void write_svg_file(struct timechart *tchart, const char *filename) |
| 1033 | { | 1451 | { |
| 1034 | u64 i; | 1452 | u64 i; |
| 1035 | int count; | 1453 | int count; |
| 1036 | int thresh = TIME_THRESH; | 1454 | int thresh = tchart->io_events ? BYTES_THRESH : TIME_THRESH; |
| 1037 | 1455 | ||
| 1038 | if (tchart->power_only) | 1456 | if (tchart->power_only) |
| 1039 | tchart->proc_num = 0; | 1457 | tchart->proc_num = 0; |
| @@ -1041,28 +1459,43 @@ static void write_svg_file(struct timechart *tchart, const char *filename) | |||
| 1041 | /* We'd like to show at least proc_num tasks; | 1459 | /* We'd like to show at least proc_num tasks; |
| 1042 | * be less picky if we have fewer */ | 1460 | * be less picky if we have fewer */ |
| 1043 | do { | 1461 | do { |
| 1044 | count = determine_display_tasks(tchart, thresh); | 1462 | if (process_filter) |
| 1463 | count = determine_display_tasks_filtered(tchart); | ||
| 1464 | else if (tchart->io_events) | ||
| 1465 | count = determine_display_io_tasks(tchart, thresh); | ||
| 1466 | else | ||
| 1467 | count = determine_display_tasks(tchart, thresh); | ||
| 1045 | thresh /= 10; | 1468 | thresh /= 10; |
| 1046 | } while (!process_filter && thresh && count < tchart->proc_num); | 1469 | } while (!process_filter && thresh && count < tchart->proc_num); |
| 1047 | 1470 | ||
| 1048 | if (!tchart->proc_num) | 1471 | if (!tchart->proc_num) |
| 1049 | count = 0; | 1472 | count = 0; |
| 1050 | 1473 | ||
| 1051 | open_svg(filename, tchart->numcpus, count, tchart->first_time, tchart->last_time); | 1474 | if (tchart->io_events) { |
| 1475 | open_svg(filename, 0, count, tchart->first_time, tchart->last_time); | ||
| 1052 | 1476 | ||
| 1053 | svg_time_grid(); | 1477 | svg_time_grid(0.5); |
| 1054 | svg_legenda(); | 1478 | svg_io_legenda(); |
| 1479 | |||
| 1480 | draw_io_bars(tchart); | ||
| 1481 | } else { | ||
| 1482 | open_svg(filename, tchart->numcpus, count, tchart->first_time, tchart->last_time); | ||
| 1055 | 1483 | ||
| 1056 | for (i = 0; i < tchart->numcpus; i++) | 1484 | svg_time_grid(0); |
| 1057 | svg_cpu_box(i, tchart->max_freq, tchart->turbo_frequency); | ||
| 1058 | 1485 | ||
| 1059 | draw_cpu_usage(tchart); | 1486 | svg_legenda(); |
| 1060 | if (tchart->proc_num) | 1487 | |
| 1061 | draw_process_bars(tchart); | 1488 | for (i = 0; i < tchart->numcpus; i++) |
| 1062 | if (!tchart->tasks_only) | 1489 | svg_cpu_box(i, tchart->max_freq, tchart->turbo_frequency); |
| 1063 | draw_c_p_states(tchart); | 1490 | |
| 1064 | if (tchart->proc_num) | 1491 | draw_cpu_usage(tchart); |
| 1065 | draw_wakeups(tchart); | 1492 | if (tchart->proc_num) |
| 1493 | draw_process_bars(tchart); | ||
| 1494 | if (!tchart->tasks_only) | ||
| 1495 | draw_c_p_states(tchart); | ||
| 1496 | if (tchart->proc_num) | ||
| 1497 | draw_wakeups(tchart); | ||
| 1498 | } | ||
| 1066 | 1499 | ||
| 1067 | svg_close(); | 1500 | svg_close(); |
| 1068 | } | 1501 | } |
| @@ -1110,6 +1543,56 @@ static int __cmd_timechart(struct timechart *tchart, const char *output_name) | |||
| 1110 | { "power:power_end", process_sample_power_end }, | 1543 | { "power:power_end", process_sample_power_end }, |
| 1111 | { "power:power_frequency", process_sample_power_frequency }, | 1544 | { "power:power_frequency", process_sample_power_frequency }, |
| 1112 | #endif | 1545 | #endif |
| 1546 | |||
| 1547 | { "syscalls:sys_enter_read", process_enter_read }, | ||
| 1548 | { "syscalls:sys_enter_pread64", process_enter_read }, | ||
| 1549 | { "syscalls:sys_enter_readv", process_enter_read }, | ||
| 1550 | { "syscalls:sys_enter_preadv", process_enter_read }, | ||
| 1551 | { "syscalls:sys_enter_write", process_enter_write }, | ||
| 1552 | { "syscalls:sys_enter_pwrite64", process_enter_write }, | ||
| 1553 | { "syscalls:sys_enter_writev", process_enter_write }, | ||
| 1554 | { "syscalls:sys_enter_pwritev", process_enter_write }, | ||
| 1555 | { "syscalls:sys_enter_sync", process_enter_sync }, | ||
| 1556 | { "syscalls:sys_enter_sync_file_range", process_enter_sync }, | ||
| 1557 | { "syscalls:sys_enter_fsync", process_enter_sync }, | ||
| 1558 | { "syscalls:sys_enter_msync", process_enter_sync }, | ||
| 1559 | { "syscalls:sys_enter_recvfrom", process_enter_rx }, | ||
| 1560 | { "syscalls:sys_enter_recvmmsg", process_enter_rx }, | ||
| 1561 | { "syscalls:sys_enter_recvmsg", process_enter_rx }, | ||
| 1562 | { "syscalls:sys_enter_sendto", process_enter_tx }, | ||
| 1563 | { "syscalls:sys_enter_sendmsg", process_enter_tx }, | ||
| 1564 | { "syscalls:sys_enter_sendmmsg", process_enter_tx }, | ||
| 1565 | { "syscalls:sys_enter_epoll_pwait", process_enter_poll }, | ||
| 1566 | { "syscalls:sys_enter_epoll_wait", process_enter_poll }, | ||
| 1567 | { "syscalls:sys_enter_poll", process_enter_poll }, | ||
| 1568 | { "syscalls:sys_enter_ppoll", process_enter_poll }, | ||
| 1569 | { "syscalls:sys_enter_pselect6", process_enter_poll }, | ||
| 1570 | { "syscalls:sys_enter_select", process_enter_poll }, | ||
| 1571 | |||
| 1572 | { "syscalls:sys_exit_read", process_exit_read }, | ||
| 1573 | { "syscalls:sys_exit_pread64", process_exit_read }, | ||
| 1574 | { "syscalls:sys_exit_readv", process_exit_read }, | ||
| 1575 | { "syscalls:sys_exit_preadv", process_exit_read }, | ||
| 1576 | { "syscalls:sys_exit_write", process_exit_write }, | ||
| 1577 | { "syscalls:sys_exit_pwrite64", process_exit_write }, | ||
| 1578 | { "syscalls:sys_exit_writev", process_exit_write }, | ||
| 1579 | { "syscalls:sys_exit_pwritev", process_exit_write }, | ||
| 1580 | { "syscalls:sys_exit_sync", process_exit_sync }, | ||
| 1581 | { "syscalls:sys_exit_sync_file_range", process_exit_sync }, | ||
| 1582 | { "syscalls:sys_exit_fsync", process_exit_sync }, | ||
| 1583 | { "syscalls:sys_exit_msync", process_exit_sync }, | ||
| 1584 | { "syscalls:sys_exit_recvfrom", process_exit_rx }, | ||
| 1585 | { "syscalls:sys_exit_recvmmsg", process_exit_rx }, | ||
| 1586 | { "syscalls:sys_exit_recvmsg", process_exit_rx }, | ||
| 1587 | { "syscalls:sys_exit_sendto", process_exit_tx }, | ||
| 1588 | { "syscalls:sys_exit_sendmsg", process_exit_tx }, | ||
| 1589 | { "syscalls:sys_exit_sendmmsg", process_exit_tx }, | ||
| 1590 | { "syscalls:sys_exit_epoll_pwait", process_exit_poll }, | ||
| 1591 | { "syscalls:sys_exit_epoll_wait", process_exit_poll }, | ||
| 1592 | { "syscalls:sys_exit_poll", process_exit_poll }, | ||
| 1593 | { "syscalls:sys_exit_ppoll", process_exit_poll }, | ||
| 1594 | { "syscalls:sys_exit_pselect6", process_exit_poll }, | ||
| 1595 | { "syscalls:sys_exit_select", process_exit_poll }, | ||
| 1113 | }; | 1596 | }; |
| 1114 | struct perf_data_file file = { | 1597 | struct perf_data_file file = { |
| 1115 | .path = input_name, | 1598 | .path = input_name, |
| @@ -1154,6 +1637,139 @@ out_delete: | |||
| 1154 | return ret; | 1637 | return ret; |
| 1155 | } | 1638 | } |
| 1156 | 1639 | ||
| 1640 | static int timechart__io_record(int argc, const char **argv) | ||
| 1641 | { | ||
| 1642 | unsigned int rec_argc, i; | ||
| 1643 | const char **rec_argv; | ||
| 1644 | const char **p; | ||
| 1645 | char *filter = NULL; | ||
| 1646 | |||
| 1647 | const char * const common_args[] = { | ||
| 1648 | "record", "-a", "-R", "-c", "1", | ||
| 1649 | }; | ||
| 1650 | unsigned int common_args_nr = ARRAY_SIZE(common_args); | ||
| 1651 | |||
| 1652 | const char * const disk_events[] = { | ||
| 1653 | "syscalls:sys_enter_read", | ||
| 1654 | "syscalls:sys_enter_pread64", | ||
| 1655 | "syscalls:sys_enter_readv", | ||
| 1656 | "syscalls:sys_enter_preadv", | ||
| 1657 | "syscalls:sys_enter_write", | ||
| 1658 | "syscalls:sys_enter_pwrite64", | ||
| 1659 | "syscalls:sys_enter_writev", | ||
| 1660 | "syscalls:sys_enter_pwritev", | ||
| 1661 | "syscalls:sys_enter_sync", | ||
| 1662 | "syscalls:sys_enter_sync_file_range", | ||
| 1663 | "syscalls:sys_enter_fsync", | ||
| 1664 | "syscalls:sys_enter_msync", | ||
| 1665 | |||
| 1666 | "syscalls:sys_exit_read", | ||
| 1667 | "syscalls:sys_exit_pread64", | ||
| 1668 | "syscalls:sys_exit_readv", | ||
| 1669 | "syscalls:sys_exit_preadv", | ||
| 1670 | "syscalls:sys_exit_write", | ||
| 1671 | "syscalls:sys_exit_pwrite64", | ||
| 1672 | "syscalls:sys_exit_writev", | ||
| 1673 | "syscalls:sys_exit_pwritev", | ||
| 1674 | "syscalls:sys_exit_sync", | ||
| 1675 | "syscalls:sys_exit_sync_file_range", | ||
| 1676 | "syscalls:sys_exit_fsync", | ||
| 1677 | "syscalls:sys_exit_msync", | ||
| 1678 | }; | ||
| 1679 | unsigned int disk_events_nr = ARRAY_SIZE(disk_events); | ||
| 1680 | |||
| 1681 | const char * const net_events[] = { | ||
| 1682 | "syscalls:sys_enter_recvfrom", | ||
| 1683 | "syscalls:sys_enter_recvmmsg", | ||
| 1684 | "syscalls:sys_enter_recvmsg", | ||
| 1685 | "syscalls:sys_enter_sendto", | ||
| 1686 | "syscalls:sys_enter_sendmsg", | ||
| 1687 | "syscalls:sys_enter_sendmmsg", | ||
| 1688 | |||
| 1689 | "syscalls:sys_exit_recvfrom", | ||
| 1690 | "syscalls:sys_exit_recvmmsg", | ||
| 1691 | "syscalls:sys_exit_recvmsg", | ||
| 1692 | "syscalls:sys_exit_sendto", | ||
| 1693 | "syscalls:sys_exit_sendmsg", | ||
| 1694 | "syscalls:sys_exit_sendmmsg", | ||
| 1695 | }; | ||
| 1696 | unsigned int net_events_nr = ARRAY_SIZE(net_events); | ||
| 1697 | |||
| 1698 | const char * const poll_events[] = { | ||
| 1699 | "syscalls:sys_enter_epoll_pwait", | ||
| 1700 | "syscalls:sys_enter_epoll_wait", | ||
| 1701 | "syscalls:sys_enter_poll", | ||
| 1702 | "syscalls:sys_enter_ppoll", | ||
| 1703 | "syscalls:sys_enter_pselect6", | ||
| 1704 | "syscalls:sys_enter_select", | ||
| 1705 | |||
| 1706 | "syscalls:sys_exit_epoll_pwait", | ||
| 1707 | "syscalls:sys_exit_epoll_wait", | ||
| 1708 | "syscalls:sys_exit_poll", | ||
| 1709 | "syscalls:sys_exit_ppoll", | ||
| 1710 | "syscalls:sys_exit_pselect6", | ||
| 1711 | "syscalls:sys_exit_select", | ||
| 1712 | }; | ||
| 1713 | unsigned int poll_events_nr = ARRAY_SIZE(poll_events); | ||
| 1714 | |||
| 1715 | rec_argc = common_args_nr + | ||
| 1716 | disk_events_nr * 4 + | ||
| 1717 | net_events_nr * 4 + | ||
| 1718 | poll_events_nr * 4 + | ||
| 1719 | argc; | ||
| 1720 | rec_argv = calloc(rec_argc + 1, sizeof(char *)); | ||
| 1721 | |||
| 1722 | if (rec_argv == NULL) | ||
| 1723 | return -ENOMEM; | ||
| 1724 | |||
| 1725 | if (asprintf(&filter, "common_pid != %d", getpid()) < 0) | ||
| 1726 | return -ENOMEM; | ||
| 1727 | |||
| 1728 | p = rec_argv; | ||
| 1729 | for (i = 0; i < common_args_nr; i++) | ||
| 1730 | *p++ = strdup(common_args[i]); | ||
| 1731 | |||
| 1732 | for (i = 0; i < disk_events_nr; i++) { | ||
| 1733 | if (!is_valid_tracepoint(disk_events[i])) { | ||
| 1734 | rec_argc -= 4; | ||
| 1735 | continue; | ||
| 1736 | } | ||
| 1737 | |||
| 1738 | *p++ = "-e"; | ||
| 1739 | *p++ = strdup(disk_events[i]); | ||
| 1740 | *p++ = "--filter"; | ||
| 1741 | *p++ = filter; | ||
| 1742 | } | ||
| 1743 | for (i = 0; i < net_events_nr; i++) { | ||
| 1744 | if (!is_valid_tracepoint(net_events[i])) { | ||
| 1745 | rec_argc -= 4; | ||
| 1746 | continue; | ||
| 1747 | } | ||
| 1748 | |||
| 1749 | *p++ = "-e"; | ||
| 1750 | *p++ = strdup(net_events[i]); | ||
| 1751 | *p++ = "--filter"; | ||
| 1752 | *p++ = filter; | ||
| 1753 | } | ||
| 1754 | for (i = 0; i < poll_events_nr; i++) { | ||
| 1755 | if (!is_valid_tracepoint(poll_events[i])) { | ||
| 1756 | rec_argc -= 4; | ||
| 1757 | continue; | ||
| 1758 | } | ||
| 1759 | |||
| 1760 | *p++ = "-e"; | ||
| 1761 | *p++ = strdup(poll_events[i]); | ||
| 1762 | *p++ = "--filter"; | ||
| 1763 | *p++ = filter; | ||
| 1764 | } | ||
| 1765 | |||
| 1766 | for (i = 0; i < (unsigned int)argc; i++) | ||
| 1767 | *p++ = argv[i]; | ||
| 1768 | |||
| 1769 | return cmd_record(rec_argc, rec_argv, NULL); | ||
| 1770 | } | ||
| 1771 | |||
| 1772 | |||
| 1157 | static int timechart__record(struct timechart *tchart, int argc, const char **argv) | 1773 | static int timechart__record(struct timechart *tchart, int argc, const char **argv) |
| 1158 | { | 1774 | { |
| 1159 | unsigned int rec_argc, i, j; | 1775 | unsigned int rec_argc, i, j; |
| @@ -1270,6 +1886,30 @@ parse_highlight(const struct option *opt __maybe_unused, const char *arg, | |||
| 1270 | return 0; | 1886 | return 0; |
| 1271 | } | 1887 | } |
| 1272 | 1888 | ||
| 1889 | static int | ||
| 1890 | parse_time(const struct option *opt, const char *arg, int __maybe_unused unset) | ||
| 1891 | { | ||
| 1892 | char unit = 'n'; | ||
| 1893 | u64 *value = opt->value; | ||
| 1894 | |||
| 1895 | if (sscanf(arg, "%" PRIu64 "%cs", value, &unit) > 0) { | ||
| 1896 | switch (unit) { | ||
| 1897 | case 'm': | ||
| 1898 | *value *= 1000000; | ||
| 1899 | break; | ||
| 1900 | case 'u': | ||
| 1901 | *value *= 1000; | ||
| 1902 | break; | ||
| 1903 | case 'n': | ||
| 1904 | break; | ||
| 1905 | default: | ||
| 1906 | return -1; | ||
| 1907 | } | ||
| 1908 | } | ||
| 1909 | |||
| 1910 | return 0; | ||
| 1911 | } | ||
| 1912 | |||
| 1273 | int cmd_timechart(int argc, const char **argv, | 1913 | int cmd_timechart(int argc, const char **argv, |
| 1274 | const char *prefix __maybe_unused) | 1914 | const char *prefix __maybe_unused) |
| 1275 | { | 1915 | { |
| @@ -1282,6 +1922,8 @@ int cmd_timechart(int argc, const char **argv, | |||
| 1282 | .ordered_samples = true, | 1922 | .ordered_samples = true, |
| 1283 | }, | 1923 | }, |
| 1284 | .proc_num = 15, | 1924 | .proc_num = 15, |
| 1925 | .min_time = 1000000, | ||
| 1926 | .merge_dist = 1000, | ||
| 1285 | }; | 1927 | }; |
| 1286 | const char *output_name = "output.svg"; | 1928 | const char *output_name = "output.svg"; |
| 1287 | const struct option timechart_options[] = { | 1929 | const struct option timechart_options[] = { |
| @@ -1303,6 +1945,14 @@ int cmd_timechart(int argc, const char **argv, | |||
| 1303 | "min. number of tasks to print"), | 1945 | "min. number of tasks to print"), |
| 1304 | OPT_BOOLEAN('t', "topology", &tchart.topology, | 1946 | OPT_BOOLEAN('t', "topology", &tchart.topology, |
| 1305 | "sort CPUs according to topology"), | 1947 | "sort CPUs according to topology"), |
| 1948 | OPT_BOOLEAN(0, "io-skip-eagain", &tchart.skip_eagain, | ||
| 1949 | "skip EAGAIN errors"), | ||
| 1950 | OPT_CALLBACK(0, "io-min-time", &tchart.min_time, "time", | ||
| 1951 | "all IO faster than min-time will visually appear longer", | ||
| 1952 | parse_time), | ||
| 1953 | OPT_CALLBACK(0, "io-merge-dist", &tchart.merge_dist, "time", | ||
| 1954 | "merge events that are merge-dist us apart", | ||
| 1955 | parse_time), | ||
| 1306 | OPT_END() | 1956 | OPT_END() |
| 1307 | }; | 1957 | }; |
| 1308 | const char * const timechart_usage[] = { | 1958 | const char * const timechart_usage[] = { |
| @@ -1314,6 +1964,8 @@ int cmd_timechart(int argc, const char **argv, | |||
| 1314 | OPT_BOOLEAN('P', "power-only", &tchart.power_only, "output power data only"), | 1964 | OPT_BOOLEAN('P', "power-only", &tchart.power_only, "output power data only"), |
| 1315 | OPT_BOOLEAN('T', "tasks-only", &tchart.tasks_only, | 1965 | OPT_BOOLEAN('T', "tasks-only", &tchart.tasks_only, |
| 1316 | "output processes data only"), | 1966 | "output processes data only"), |
| 1967 | OPT_BOOLEAN('I', "io-only", &tchart.io_only, | ||
| 1968 | "record only IO data"), | ||
| 1317 | OPT_BOOLEAN('g', "callchain", &tchart.with_backtrace, "record callchain"), | 1969 | OPT_BOOLEAN('g', "callchain", &tchart.with_backtrace, "record callchain"), |
| 1318 | OPT_END() | 1970 | OPT_END() |
| 1319 | }; | 1971 | }; |
| @@ -1340,7 +1992,10 @@ int cmd_timechart(int argc, const char **argv, | |||
| 1340 | return -1; | 1992 | return -1; |
| 1341 | } | 1993 | } |
| 1342 | 1994 | ||
| 1343 | return timechart__record(&tchart, argc, argv); | 1995 | if (tchart.io_only) |
| 1996 | return timechart__io_record(argc, argv); | ||
| 1997 | else | ||
| 1998 | return timechart__record(&tchart, argc, argv); | ||
| 1344 | } else if (argc) | 1999 | } else if (argc) |
| 1345 | usage_with_options(timechart_usage, timechart_options); | 2000 | usage_with_options(timechart_usage, timechart_options); |
| 1346 | 2001 | ||
diff --git a/tools/perf/util/svghelper.c b/tools/perf/util/svghelper.c index 6a0a13d07a28..283d3e73e2f2 100644 --- a/tools/perf/util/svghelper.c +++ b/tools/perf/util/svghelper.c | |||
| @@ -30,6 +30,7 @@ static u64 turbo_frequency, max_freq; | |||
| 30 | 30 | ||
| 31 | #define SLOT_MULT 30.0 | 31 | #define SLOT_MULT 30.0 |
| 32 | #define SLOT_HEIGHT 25.0 | 32 | #define SLOT_HEIGHT 25.0 |
| 33 | #define SLOT_HALF (SLOT_HEIGHT / 2) | ||
| 33 | 34 | ||
| 34 | int svg_page_width = 1000; | 35 | int svg_page_width = 1000; |
| 35 | u64 svg_highlight; | 36 | u64 svg_highlight; |
| @@ -114,8 +115,14 @@ void open_svg(const char *filename, int cpus, int rows, u64 start, u64 end) | |||
| 114 | fprintf(svgfile, " rect { stroke-width: 1; }\n"); | 115 | fprintf(svgfile, " rect { stroke-width: 1; }\n"); |
| 115 | fprintf(svgfile, " rect.process { fill:rgb(180,180,180); fill-opacity:0.9; stroke-width:1; stroke:rgb( 0, 0, 0); } \n"); | 116 | fprintf(svgfile, " rect.process { fill:rgb(180,180,180); fill-opacity:0.9; stroke-width:1; stroke:rgb( 0, 0, 0); } \n"); |
| 116 | fprintf(svgfile, " rect.process2 { fill:rgb(180,180,180); fill-opacity:0.9; stroke-width:0; stroke:rgb( 0, 0, 0); } \n"); | 117 | fprintf(svgfile, " rect.process2 { fill:rgb(180,180,180); fill-opacity:0.9; stroke-width:0; stroke:rgb( 0, 0, 0); } \n"); |
| 118 | fprintf(svgfile, " rect.process3 { fill:rgb(180,180,180); fill-opacity:0.5; stroke-width:0; stroke:rgb( 0, 0, 0); } \n"); | ||
| 117 | fprintf(svgfile, " rect.sample { fill:rgb( 0, 0,255); fill-opacity:0.8; stroke-width:0; stroke:rgb( 0, 0, 0); } \n"); | 119 | fprintf(svgfile, " rect.sample { fill:rgb( 0, 0,255); fill-opacity:0.8; stroke-width:0; stroke:rgb( 0, 0, 0); } \n"); |
| 118 | fprintf(svgfile, " rect.sample_hi{ fill:rgb(255,128, 0); fill-opacity:0.8; stroke-width:0; stroke:rgb( 0, 0, 0); } \n"); | 120 | fprintf(svgfile, " rect.sample_hi{ fill:rgb(255,128, 0); fill-opacity:0.8; stroke-width:0; stroke:rgb( 0, 0, 0); } \n"); |
| 121 | fprintf(svgfile, " rect.error { fill:rgb(255, 0, 0); fill-opacity:0.5; stroke-width:0; stroke:rgb( 0, 0, 0); } \n"); | ||
| 122 | fprintf(svgfile, " rect.net { fill:rgb( 0,128, 0); fill-opacity:0.5; stroke-width:0; stroke:rgb( 0, 0, 0); } \n"); | ||
| 123 | fprintf(svgfile, " rect.disk { fill:rgb( 0, 0,255); fill-opacity:0.5; stroke-width:0; stroke:rgb( 0, 0, 0); } \n"); | ||
| 124 | fprintf(svgfile, " rect.sync { fill:rgb(128,128, 0); fill-opacity:0.5; stroke-width:0; stroke:rgb( 0, 0, 0); } \n"); | ||
| 125 | fprintf(svgfile, " rect.poll { fill:rgb( 0,128,128); fill-opacity:0.2; stroke-width:0; stroke:rgb( 0, 0, 0); } \n"); | ||
| 119 | fprintf(svgfile, " rect.blocked { fill:rgb(255, 0, 0); fill-opacity:0.5; stroke-width:0; stroke:rgb( 0, 0, 0); } \n"); | 126 | fprintf(svgfile, " rect.blocked { fill:rgb(255, 0, 0); fill-opacity:0.5; stroke-width:0; stroke:rgb( 0, 0, 0); } \n"); |
| 120 | fprintf(svgfile, " rect.waiting { fill:rgb(224,214, 0); fill-opacity:0.8; stroke-width:0; stroke:rgb( 0, 0, 0); } \n"); | 127 | fprintf(svgfile, " rect.waiting { fill:rgb(224,214, 0); fill-opacity:0.8; stroke-width:0; stroke:rgb( 0, 0, 0); } \n"); |
| 121 | fprintf(svgfile, " rect.WAITING { fill:rgb(255,214, 48); fill-opacity:0.6; stroke-width:0; stroke:rgb( 0, 0, 0); } \n"); | 128 | fprintf(svgfile, " rect.WAITING { fill:rgb(255,214, 48); fill-opacity:0.6; stroke-width:0; stroke:rgb( 0, 0, 0); } \n"); |
| @@ -132,12 +139,81 @@ void open_svg(const char *filename, int cpus, int rows, u64 start, u64 end) | |||
| 132 | fprintf(svgfile, " ]]>\n </style>\n</defs>\n"); | 139 | fprintf(svgfile, " ]]>\n </style>\n</defs>\n"); |
| 133 | } | 140 | } |
| 134 | 141 | ||
| 142 | static double normalize_height(double height) | ||
| 143 | { | ||
| 144 | if (height < 0.25) | ||
| 145 | return 0.25; | ||
| 146 | else if (height < 0.50) | ||
| 147 | return 0.50; | ||
| 148 | else if (height < 0.75) | ||
| 149 | return 0.75; | ||
| 150 | else | ||
| 151 | return 0.100; | ||
| 152 | } | ||
| 153 | |||
| 154 | void svg_ubox(int Yslot, u64 start, u64 end, double height, const char *type, int fd, int err, int merges) | ||
| 155 | { | ||
| 156 | double w = time2pixels(end) - time2pixels(start); | ||
| 157 | height = normalize_height(height); | ||
| 158 | |||
| 159 | if (!svgfile) | ||
| 160 | return; | ||
| 161 | |||
| 162 | fprintf(svgfile, "<g>\n"); | ||
| 163 | fprintf(svgfile, "<title>fd=%d error=%d merges=%d</title>\n", fd, err, merges); | ||
| 164 | fprintf(svgfile, "<rect x=\"%.8f\" width=\"%.8f\" y=\"%.1f\" height=\"%.1f\" class=\"%s\"/>\n", | ||
| 165 | time2pixels(start), | ||
| 166 | w, | ||
| 167 | Yslot * SLOT_MULT, | ||
| 168 | SLOT_HALF * height, | ||
| 169 | type); | ||
| 170 | fprintf(svgfile, "</g>\n"); | ||
| 171 | } | ||
| 172 | |||
| 173 | void svg_lbox(int Yslot, u64 start, u64 end, double height, const char *type, int fd, int err, int merges) | ||
| 174 | { | ||
| 175 | double w = time2pixels(end) - time2pixels(start); | ||
| 176 | height = normalize_height(height); | ||
| 177 | |||
| 178 | if (!svgfile) | ||
| 179 | return; | ||
| 180 | |||
| 181 | fprintf(svgfile, "<g>\n"); | ||
| 182 | fprintf(svgfile, "<title>fd=%d error=%d merges=%d</title>\n", fd, err, merges); | ||
| 183 | fprintf(svgfile, "<rect x=\"%.8f\" width=\"%.8f\" y=\"%.1f\" height=\"%.1f\" class=\"%s\"/>\n", | ||
| 184 | time2pixels(start), | ||
| 185 | w, | ||
| 186 | Yslot * SLOT_MULT + SLOT_HEIGHT - SLOT_HALF * height, | ||
| 187 | SLOT_HALF * height, | ||
| 188 | type); | ||
| 189 | fprintf(svgfile, "</g>\n"); | ||
| 190 | } | ||
| 191 | |||
| 192 | void svg_fbox(int Yslot, u64 start, u64 end, double height, const char *type, int fd, int err, int merges) | ||
| 193 | { | ||
| 194 | double w = time2pixels(end) - time2pixels(start); | ||
| 195 | height = normalize_height(height); | ||
| 196 | |||
| 197 | if (!svgfile) | ||
| 198 | return; | ||
| 199 | |||
| 200 | fprintf(svgfile, "<g>\n"); | ||
| 201 | fprintf(svgfile, "<title>fd=%d error=%d merges=%d</title>\n", fd, err, merges); | ||
| 202 | fprintf(svgfile, "<rect x=\"%.8f\" width=\"%.8f\" y=\"%.1f\" height=\"%.1f\" class=\"%s\"/>\n", | ||
| 203 | time2pixels(start), | ||
| 204 | w, | ||
| 205 | Yslot * SLOT_MULT + SLOT_HEIGHT - SLOT_HEIGHT * height, | ||
| 206 | SLOT_HEIGHT * height, | ||
| 207 | type); | ||
| 208 | fprintf(svgfile, "</g>\n"); | ||
| 209 | } | ||
| 210 | |||
| 135 | void svg_box(int Yslot, u64 start, u64 end, const char *type) | 211 | void svg_box(int Yslot, u64 start, u64 end, const char *type) |
| 136 | { | 212 | { |
| 137 | if (!svgfile) | 213 | if (!svgfile) |
| 138 | return; | 214 | return; |
| 139 | 215 | ||
| 140 | fprintf(svgfile, "<rect x=\"%4.8f\" width=\"%4.8f\" y=\"%4.1f\" height=\"%4.1f\" class=\"%s\"/>\n", | 216 | fprintf(svgfile, "<rect x=\"%.8f\" width=\"%.8f\" y=\"%.1f\" height=\"%.1f\" class=\"%s\"/>\n", |
| 141 | time2pixels(start), time2pixels(end)-time2pixels(start), Yslot * SLOT_MULT, SLOT_HEIGHT, type); | 217 | time2pixels(start), time2pixels(end)-time2pixels(start), Yslot * SLOT_MULT, SLOT_HEIGHT, type); |
| 142 | } | 218 | } |
| 143 | 219 | ||
| @@ -174,7 +250,7 @@ void svg_running(int Yslot, int cpu, u64 start, u64 end, const char *backtrace) | |||
| 174 | cpu, time_to_string(end - start)); | 250 | cpu, time_to_string(end - start)); |
| 175 | if (backtrace) | 251 | if (backtrace) |
| 176 | fprintf(svgfile, "<desc>Switched because:\n%s</desc>\n", backtrace); | 252 | fprintf(svgfile, "<desc>Switched because:\n%s</desc>\n", backtrace); |
| 177 | fprintf(svgfile, "<rect x=\"%4.8f\" width=\"%4.8f\" y=\"%4.1f\" height=\"%4.1f\" class=\"%s\"/>\n", | 253 | fprintf(svgfile, "<rect x=\"%.8f\" width=\"%.8f\" y=\"%.1f\" height=\"%.1f\" class=\"%s\"/>\n", |
| 178 | time2pixels(start), time2pixels(end)-time2pixels(start), Yslot * SLOT_MULT, SLOT_HEIGHT, | 254 | time2pixels(start), time2pixels(end)-time2pixels(start), Yslot * SLOT_MULT, SLOT_HEIGHT, |
| 179 | type); | 255 | type); |
| 180 | 256 | ||
| @@ -186,7 +262,7 @@ void svg_running(int Yslot, int cpu, u64 start, u64 end, const char *backtrace) | |||
| 186 | text_size = round_text_size(text_size); | 262 | text_size = round_text_size(text_size); |
| 187 | 263 | ||
| 188 | if (text_size > MIN_TEXT_SIZE) | 264 | if (text_size > MIN_TEXT_SIZE) |
| 189 | fprintf(svgfile, "<text x=\"%1.8f\" y=\"%1.8f\" font-size=\"%1.8fpt\">%i</text>\n", | 265 | fprintf(svgfile, "<text x=\"%.8f\" y=\"%.8f\" font-size=\"%.8fpt\">%i</text>\n", |
| 190 | time2pixels(start), Yslot * SLOT_MULT + SLOT_HEIGHT - 1, text_size, cpu + 1); | 266 | time2pixels(start), Yslot * SLOT_MULT + SLOT_HEIGHT - 1, text_size, cpu + 1); |
| 191 | 267 | ||
| 192 | fprintf(svgfile, "</g>\n"); | 268 | fprintf(svgfile, "</g>\n"); |
| @@ -202,10 +278,10 @@ static char *time_to_string(u64 duration) | |||
| 202 | return text; | 278 | return text; |
| 203 | 279 | ||
| 204 | if (duration < 1000 * 1000) { /* less than 1 msec */ | 280 | if (duration < 1000 * 1000) { /* less than 1 msec */ |
| 205 | sprintf(text, "%4.1f us", duration / 1000.0); | 281 | sprintf(text, "%.1f us", duration / 1000.0); |
| 206 | return text; | 282 | return text; |
| 207 | } | 283 | } |
| 208 | sprintf(text, "%4.1f ms", duration / 1000.0 / 1000); | 284 | sprintf(text, "%.1f ms", duration / 1000.0 / 1000); |
| 209 | 285 | ||
| 210 | return text; | 286 | return text; |
| 211 | } | 287 | } |
| @@ -233,14 +309,14 @@ void svg_waiting(int Yslot, int cpu, u64 start, u64 end, const char *backtrace) | |||
| 233 | 309 | ||
| 234 | font_size = round_text_size(font_size); | 310 | font_size = round_text_size(font_size); |
| 235 | 311 | ||
| 236 | fprintf(svgfile, "<g transform=\"translate(%4.8f,%4.8f)\">\n", time2pixels(start), Yslot * SLOT_MULT); | 312 | fprintf(svgfile, "<g transform=\"translate(%.8f,%.8f)\">\n", time2pixels(start), Yslot * SLOT_MULT); |
| 237 | fprintf(svgfile, "<title>#%d waiting %s</title>\n", cpu, time_to_string(end - start)); | 313 | fprintf(svgfile, "<title>#%d waiting %s</title>\n", cpu, time_to_string(end - start)); |
| 238 | if (backtrace) | 314 | if (backtrace) |
| 239 | fprintf(svgfile, "<desc>Waiting on:\n%s</desc>\n", backtrace); | 315 | fprintf(svgfile, "<desc>Waiting on:\n%s</desc>\n", backtrace); |
| 240 | fprintf(svgfile, "<rect x=\"0\" width=\"%4.8f\" y=\"0\" height=\"%4.1f\" class=\"%s\"/>\n", | 316 | fprintf(svgfile, "<rect x=\"0\" width=\"%.8f\" y=\"0\" height=\"%.1f\" class=\"%s\"/>\n", |
| 241 | time2pixels(end)-time2pixels(start), SLOT_HEIGHT, style); | 317 | time2pixels(end)-time2pixels(start), SLOT_HEIGHT, style); |
| 242 | if (font_size > MIN_TEXT_SIZE) | 318 | if (font_size > MIN_TEXT_SIZE) |
| 243 | fprintf(svgfile, "<text transform=\"rotate(90)\" font-size=\"%1.8fpt\"> %s</text>\n", | 319 | fprintf(svgfile, "<text transform=\"rotate(90)\" font-size=\"%.8fpt\"> %s</text>\n", |
| 244 | font_size, text); | 320 | font_size, text); |
| 245 | fprintf(svgfile, "</g>\n"); | 321 | fprintf(svgfile, "</g>\n"); |
| 246 | } | 322 | } |
| @@ -289,16 +365,16 @@ void svg_cpu_box(int cpu, u64 __max_freq, u64 __turbo_freq) | |||
| 289 | 365 | ||
| 290 | fprintf(svgfile, "<g>\n"); | 366 | fprintf(svgfile, "<g>\n"); |
| 291 | 367 | ||
| 292 | fprintf(svgfile, "<rect x=\"%4.8f\" width=\"%4.8f\" y=\"%4.1f\" height=\"%4.1f\" class=\"cpu\"/>\n", | 368 | fprintf(svgfile, "<rect x=\"%.8f\" width=\"%.8f\" y=\"%.1f\" height=\"%.1f\" class=\"cpu\"/>\n", |
| 293 | time2pixels(first_time), | 369 | time2pixels(first_time), |
| 294 | time2pixels(last_time)-time2pixels(first_time), | 370 | time2pixels(last_time)-time2pixels(first_time), |
| 295 | cpu2y(cpu), SLOT_MULT+SLOT_HEIGHT); | 371 | cpu2y(cpu), SLOT_MULT+SLOT_HEIGHT); |
| 296 | 372 | ||
| 297 | sprintf(cpu_string, "CPU %i", (int)cpu); | 373 | sprintf(cpu_string, "CPU %i", (int)cpu); |
| 298 | fprintf(svgfile, "<text x=\"%4.8f\" y=\"%4.8f\">%s</text>\n", | 374 | fprintf(svgfile, "<text x=\"%.8f\" y=\"%.8f\">%s</text>\n", |
| 299 | 10+time2pixels(first_time), cpu2y(cpu) + SLOT_HEIGHT/2, cpu_string); | 375 | 10+time2pixels(first_time), cpu2y(cpu) + SLOT_HEIGHT/2, cpu_string); |
| 300 | 376 | ||
| 301 | fprintf(svgfile, "<text transform=\"translate(%4.8f,%4.8f)\" font-size=\"1.25pt\">%s</text>\n", | 377 | fprintf(svgfile, "<text transform=\"translate(%.8f,%.8f)\" font-size=\"1.25pt\">%s</text>\n", |
| 302 | 10+time2pixels(first_time), cpu2y(cpu) + SLOT_MULT + SLOT_HEIGHT - 4, cpu_model()); | 378 | 10+time2pixels(first_time), cpu2y(cpu) + SLOT_MULT + SLOT_HEIGHT - 4, cpu_model()); |
| 303 | 379 | ||
| 304 | fprintf(svgfile, "</g>\n"); | 380 | fprintf(svgfile, "</g>\n"); |
| @@ -319,11 +395,11 @@ void svg_process(int cpu, u64 start, u64 end, int pid, const char *name, const c | |||
| 319 | else | 395 | else |
| 320 | type = "sample"; | 396 | type = "sample"; |
| 321 | 397 | ||
| 322 | fprintf(svgfile, "<g transform=\"translate(%4.8f,%4.8f)\">\n", time2pixels(start), cpu2y(cpu)); | 398 | fprintf(svgfile, "<g transform=\"translate(%.8f,%.8f)\">\n", time2pixels(start), cpu2y(cpu)); |
| 323 | fprintf(svgfile, "<title>%d %s running %s</title>\n", pid, name, time_to_string(end - start)); | 399 | fprintf(svgfile, "<title>%d %s running %s</title>\n", pid, name, time_to_string(end - start)); |
| 324 | if (backtrace) | 400 | if (backtrace) |
| 325 | fprintf(svgfile, "<desc>Switched because:\n%s</desc>\n", backtrace); | 401 | fprintf(svgfile, "<desc>Switched because:\n%s</desc>\n", backtrace); |
| 326 | fprintf(svgfile, "<rect x=\"0\" width=\"%4.8f\" y=\"0\" height=\"%4.1f\" class=\"%s\"/>\n", | 402 | fprintf(svgfile, "<rect x=\"0\" width=\"%.8f\" y=\"0\" height=\"%.1f\" class=\"%s\"/>\n", |
| 327 | time2pixels(end)-time2pixels(start), SLOT_MULT+SLOT_HEIGHT, type); | 403 | time2pixels(end)-time2pixels(start), SLOT_MULT+SLOT_HEIGHT, type); |
| 328 | width = time2pixels(end)-time2pixels(start); | 404 | width = time2pixels(end)-time2pixels(start); |
| 329 | if (width > 6) | 405 | if (width > 6) |
| @@ -332,7 +408,7 @@ void svg_process(int cpu, u64 start, u64 end, int pid, const char *name, const c | |||
| 332 | width = round_text_size(width); | 408 | width = round_text_size(width); |
| 333 | 409 | ||
| 334 | if (width > MIN_TEXT_SIZE) | 410 | if (width > MIN_TEXT_SIZE) |
| 335 | fprintf(svgfile, "<text transform=\"rotate(90)\" font-size=\"%3.8fpt\">%s</text>\n", | 411 | fprintf(svgfile, "<text transform=\"rotate(90)\" font-size=\"%.8fpt\">%s</text>\n", |
| 336 | width, name); | 412 | width, name); |
| 337 | 413 | ||
| 338 | fprintf(svgfile, "</g>\n"); | 414 | fprintf(svgfile, "</g>\n"); |
| @@ -353,7 +429,7 @@ void svg_cstate(int cpu, u64 start, u64 end, int type) | |||
| 353 | type = 6; | 429 | type = 6; |
| 354 | sprintf(style, "c%i", type); | 430 | sprintf(style, "c%i", type); |
| 355 | 431 | ||
| 356 | fprintf(svgfile, "<rect class=\"%s\" x=\"%4.8f\" width=\"%4.8f\" y=\"%4.1f\" height=\"%4.1f\"/>\n", | 432 | fprintf(svgfile, "<rect class=\"%s\" x=\"%.8f\" width=\"%.8f\" y=\"%.1f\" height=\"%.1f\"/>\n", |
| 357 | style, | 433 | style, |
| 358 | time2pixels(start), time2pixels(end)-time2pixels(start), | 434 | time2pixels(start), time2pixels(end)-time2pixels(start), |
| 359 | cpu2y(cpu), SLOT_MULT+SLOT_HEIGHT); | 435 | cpu2y(cpu), SLOT_MULT+SLOT_HEIGHT); |
| @@ -365,7 +441,7 @@ void svg_cstate(int cpu, u64 start, u64 end, int type) | |||
| 365 | width = round_text_size(width); | 441 | width = round_text_size(width); |
| 366 | 442 | ||
| 367 | if (width > MIN_TEXT_SIZE) | 443 | if (width > MIN_TEXT_SIZE) |
| 368 | fprintf(svgfile, "<text x=\"%4.8f\" y=\"%4.8f\" font-size=\"%3.8fpt\">C%i</text>\n", | 444 | fprintf(svgfile, "<text x=\"%.8f\" y=\"%.8f\" font-size=\"%.8fpt\">C%i</text>\n", |
| 369 | time2pixels(start), cpu2y(cpu)+width, width, type); | 445 | time2pixels(start), cpu2y(cpu)+width, width, type); |
| 370 | 446 | ||
| 371 | fprintf(svgfile, "</g>\n"); | 447 | fprintf(svgfile, "</g>\n"); |
| @@ -407,9 +483,9 @@ void svg_pstate(int cpu, u64 start, u64 end, u64 freq) | |||
| 407 | if (max_freq) | 483 | if (max_freq) |
| 408 | height = freq * 1.0 / max_freq * (SLOT_HEIGHT + SLOT_MULT); | 484 | height = freq * 1.0 / max_freq * (SLOT_HEIGHT + SLOT_MULT); |
| 409 | height = 1 + cpu2y(cpu) + SLOT_MULT + SLOT_HEIGHT - height; | 485 | height = 1 + cpu2y(cpu) + SLOT_MULT + SLOT_HEIGHT - height; |
| 410 | fprintf(svgfile, "<line x1=\"%4.8f\" x2=\"%4.8f\" y1=\"%4.1f\" y2=\"%4.1f\" class=\"pstate\"/>\n", | 486 | fprintf(svgfile, "<line x1=\"%.8f\" x2=\"%.8f\" y1=\"%.1f\" y2=\"%.1f\" class=\"pstate\"/>\n", |
| 411 | time2pixels(start), time2pixels(end), height, height); | 487 | time2pixels(start), time2pixels(end), height, height); |
| 412 | fprintf(svgfile, "<text x=\"%4.8f\" y=\"%4.8f\" font-size=\"0.25pt\">%s</text>\n", | 488 | fprintf(svgfile, "<text x=\"%.8f\" y=\"%.8f\" font-size=\"0.25pt\">%s</text>\n", |
| 413 | time2pixels(start), height+0.9, HzToHuman(freq)); | 489 | time2pixels(start), height+0.9, HzToHuman(freq)); |
| 414 | 490 | ||
| 415 | fprintf(svgfile, "</g>\n"); | 491 | fprintf(svgfile, "</g>\n"); |
| @@ -435,32 +511,32 @@ void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc | |||
| 435 | 511 | ||
| 436 | if (row1 < row2) { | 512 | if (row1 < row2) { |
| 437 | if (row1) { | 513 | if (row1) { |
| 438 | fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%4.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n", | 514 | fprintf(svgfile, "<line x1=\"%.8f\" y1=\"%.2f\" x2=\"%.8f\" y2=\"%.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n", |
| 439 | time2pixels(start), row1 * SLOT_MULT + SLOT_HEIGHT, time2pixels(start), row1 * SLOT_MULT + SLOT_HEIGHT + SLOT_MULT/32); | 515 | time2pixels(start), row1 * SLOT_MULT + SLOT_HEIGHT, time2pixels(start), row1 * SLOT_MULT + SLOT_HEIGHT + SLOT_MULT/32); |
| 440 | if (desc2) | 516 | if (desc2) |
| 441 | fprintf(svgfile, "<g transform=\"translate(%4.8f,%4.8f)\"><text transform=\"rotate(90)\" font-size=\"0.02pt\">%s ></text></g>\n", | 517 | fprintf(svgfile, "<g transform=\"translate(%.8f,%.8f)\"><text transform=\"rotate(90)\" font-size=\"0.02pt\">%s ></text></g>\n", |
| 442 | time2pixels(start), row1 * SLOT_MULT + SLOT_HEIGHT + SLOT_HEIGHT/48, desc2); | 518 | time2pixels(start), row1 * SLOT_MULT + SLOT_HEIGHT + SLOT_HEIGHT/48, desc2); |
| 443 | } | 519 | } |
| 444 | if (row2) { | 520 | if (row2) { |
| 445 | fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%4.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n", | 521 | fprintf(svgfile, "<line x1=\"%.8f\" y1=\"%.2f\" x2=\"%.8f\" y2=\"%.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n", |
| 446 | time2pixels(start), row2 * SLOT_MULT - SLOT_MULT/32, time2pixels(start), row2 * SLOT_MULT); | 522 | time2pixels(start), row2 * SLOT_MULT - SLOT_MULT/32, time2pixels(start), row2 * SLOT_MULT); |
| 447 | if (desc1) | 523 | if (desc1) |
| 448 | fprintf(svgfile, "<g transform=\"translate(%4.8f,%4.8f)\"><text transform=\"rotate(90)\" font-size=\"0.02pt\">%s ></text></g>\n", | 524 | fprintf(svgfile, "<g transform=\"translate(%.8f,%.8f)\"><text transform=\"rotate(90)\" font-size=\"0.02pt\">%s ></text></g>\n", |
| 449 | time2pixels(start), row2 * SLOT_MULT - SLOT_MULT/32, desc1); | 525 | time2pixels(start), row2 * SLOT_MULT - SLOT_MULT/32, desc1); |
| 450 | } | 526 | } |
| 451 | } else { | 527 | } else { |
| 452 | if (row2) { | 528 | if (row2) { |
| 453 | fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%4.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n", | 529 | fprintf(svgfile, "<line x1=\"%.8f\" y1=\"%.2f\" x2=\"%.8f\" y2=\"%.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n", |
| 454 | time2pixels(start), row2 * SLOT_MULT + SLOT_HEIGHT, time2pixels(start), row2 * SLOT_MULT + SLOT_HEIGHT + SLOT_MULT/32); | 530 | time2pixels(start), row2 * SLOT_MULT + SLOT_HEIGHT, time2pixels(start), row2 * SLOT_MULT + SLOT_HEIGHT + SLOT_MULT/32); |
| 455 | if (desc1) | 531 | if (desc1) |
| 456 | fprintf(svgfile, "<g transform=\"translate(%4.8f,%4.8f)\"><text transform=\"rotate(90)\" font-size=\"0.02pt\">%s <</text></g>\n", | 532 | fprintf(svgfile, "<g transform=\"translate(%.8f,%.8f)\"><text transform=\"rotate(90)\" font-size=\"0.02pt\">%s <</text></g>\n", |
| 457 | time2pixels(start), row2 * SLOT_MULT + SLOT_HEIGHT + SLOT_MULT/48, desc1); | 533 | time2pixels(start), row2 * SLOT_MULT + SLOT_HEIGHT + SLOT_MULT/48, desc1); |
| 458 | } | 534 | } |
| 459 | if (row1) { | 535 | if (row1) { |
| 460 | fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%4.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n", | 536 | fprintf(svgfile, "<line x1=\"%.8f\" y1=\"%.2f\" x2=\"%.8f\" y2=\"%.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n", |
| 461 | time2pixels(start), row1 * SLOT_MULT - SLOT_MULT/32, time2pixels(start), row1 * SLOT_MULT); | 537 | time2pixels(start), row1 * SLOT_MULT - SLOT_MULT/32, time2pixels(start), row1 * SLOT_MULT); |
| 462 | if (desc2) | 538 | if (desc2) |
| 463 | fprintf(svgfile, "<g transform=\"translate(%4.8f,%4.8f)\"><text transform=\"rotate(90)\" font-size=\"0.02pt\">%s <</text></g>\n", | 539 | fprintf(svgfile, "<g transform=\"translate(%.8f,%.8f)\"><text transform=\"rotate(90)\" font-size=\"0.02pt\">%s <</text></g>\n", |
| 464 | time2pixels(start), row1 * SLOT_MULT - SLOT_HEIGHT/32, desc2); | 540 | time2pixels(start), row1 * SLOT_MULT - SLOT_HEIGHT/32, desc2); |
| 465 | } | 541 | } |
| 466 | } | 542 | } |
| @@ -468,7 +544,7 @@ void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc | |||
| 468 | if (row2 > row1) | 544 | if (row2 > row1) |
| 469 | height += SLOT_HEIGHT; | 545 | height += SLOT_HEIGHT; |
| 470 | if (row1) | 546 | if (row1) |
| 471 | fprintf(svgfile, "<circle cx=\"%4.8f\" cy=\"%4.2f\" r = \"0.01\" style=\"fill:rgb(32,255,32)\"/>\n", | 547 | fprintf(svgfile, "<circle cx=\"%.8f\" cy=\"%.2f\" r = \"0.01\" style=\"fill:rgb(32,255,32)\"/>\n", |
| 472 | time2pixels(start), height); | 548 | time2pixels(start), height); |
| 473 | 549 | ||
| 474 | fprintf(svgfile, "</g>\n"); | 550 | fprintf(svgfile, "</g>\n"); |
| @@ -488,16 +564,16 @@ void svg_wakeline(u64 start, int row1, int row2, const char *backtrace) | |||
| 488 | fprintf(svgfile, "<desc>%s</desc>\n", backtrace); | 564 | fprintf(svgfile, "<desc>%s</desc>\n", backtrace); |
| 489 | 565 | ||
| 490 | if (row1 < row2) | 566 | if (row1 < row2) |
| 491 | fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%4.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n", | 567 | fprintf(svgfile, "<line x1=\"%.8f\" y1=\"%.2f\" x2=\"%.8f\" y2=\"%.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n", |
| 492 | time2pixels(start), row1 * SLOT_MULT + SLOT_HEIGHT, time2pixels(start), row2 * SLOT_MULT); | 568 | time2pixels(start), row1 * SLOT_MULT + SLOT_HEIGHT, time2pixels(start), row2 * SLOT_MULT); |
| 493 | else | 569 | else |
| 494 | fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%4.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n", | 570 | fprintf(svgfile, "<line x1=\"%.8f\" y1=\"%.2f\" x2=\"%.8f\" y2=\"%.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n", |
| 495 | time2pixels(start), row2 * SLOT_MULT + SLOT_HEIGHT, time2pixels(start), row1 * SLOT_MULT); | 571 | time2pixels(start), row2 * SLOT_MULT + SLOT_HEIGHT, time2pixels(start), row1 * SLOT_MULT); |
| 496 | 572 | ||
| 497 | height = row1 * SLOT_MULT; | 573 | height = row1 * SLOT_MULT; |
| 498 | if (row2 > row1) | 574 | if (row2 > row1) |
| 499 | height += SLOT_HEIGHT; | 575 | height += SLOT_HEIGHT; |
| 500 | fprintf(svgfile, "<circle cx=\"%4.8f\" cy=\"%4.2f\" r = \"0.01\" style=\"fill:rgb(32,255,32)\"/>\n", | 576 | fprintf(svgfile, "<circle cx=\"%.8f\" cy=\"%.2f\" r = \"0.01\" style=\"fill:rgb(32,255,32)\"/>\n", |
| 501 | time2pixels(start), height); | 577 | time2pixels(start), height); |
| 502 | 578 | ||
| 503 | fprintf(svgfile, "</g>\n"); | 579 | fprintf(svgfile, "</g>\n"); |
| @@ -515,9 +591,9 @@ void svg_interrupt(u64 start, int row, const char *backtrace) | |||
| 515 | if (backtrace) | 591 | if (backtrace) |
| 516 | fprintf(svgfile, "<desc>%s</desc>\n", backtrace); | 592 | fprintf(svgfile, "<desc>%s</desc>\n", backtrace); |
| 517 | 593 | ||
| 518 | fprintf(svgfile, "<circle cx=\"%4.8f\" cy=\"%4.2f\" r = \"0.01\" style=\"fill:rgb(255,128,128)\"/>\n", | 594 | fprintf(svgfile, "<circle cx=\"%.8f\" cy=\"%.2f\" r = \"0.01\" style=\"fill:rgb(255,128,128)\"/>\n", |
| 519 | time2pixels(start), row * SLOT_MULT); | 595 | time2pixels(start), row * SLOT_MULT); |
| 520 | fprintf(svgfile, "<circle cx=\"%4.8f\" cy=\"%4.2f\" r = \"0.01\" style=\"fill:rgb(255,128,128)\"/>\n", | 596 | fprintf(svgfile, "<circle cx=\"%.8f\" cy=\"%.2f\" r = \"0.01\" style=\"fill:rgb(255,128,128)\"/>\n", |
| 521 | time2pixels(start), row * SLOT_MULT + SLOT_HEIGHT); | 597 | time2pixels(start), row * SLOT_MULT + SLOT_HEIGHT); |
| 522 | 598 | ||
| 523 | fprintf(svgfile, "</g>\n"); | 599 | fprintf(svgfile, "</g>\n"); |
| @@ -528,7 +604,7 @@ void svg_text(int Yslot, u64 start, const char *text) | |||
| 528 | if (!svgfile) | 604 | if (!svgfile) |
| 529 | return; | 605 | return; |
| 530 | 606 | ||
| 531 | fprintf(svgfile, "<text x=\"%4.8f\" y=\"%4.8f\">%s</text>\n", | 607 | fprintf(svgfile, "<text x=\"%.8f\" y=\"%.8f\">%s</text>\n", |
| 532 | time2pixels(start), Yslot * SLOT_MULT+SLOT_HEIGHT/2, text); | 608 | time2pixels(start), Yslot * SLOT_MULT+SLOT_HEIGHT/2, text); |
| 533 | } | 609 | } |
| 534 | 610 | ||
| @@ -537,12 +613,26 @@ static void svg_legenda_box(int X, const char *text, const char *style) | |||
| 537 | double boxsize; | 613 | double boxsize; |
| 538 | boxsize = SLOT_HEIGHT / 2; | 614 | boxsize = SLOT_HEIGHT / 2; |
| 539 | 615 | ||
| 540 | fprintf(svgfile, "<rect x=\"%i\" width=\"%4.8f\" y=\"0\" height=\"%4.1f\" class=\"%s\"/>\n", | 616 | fprintf(svgfile, "<rect x=\"%i\" width=\"%.8f\" y=\"0\" height=\"%.1f\" class=\"%s\"/>\n", |
| 541 | X, boxsize, boxsize, style); | 617 | X, boxsize, boxsize, style); |
| 542 | fprintf(svgfile, "<text transform=\"translate(%4.8f, %4.8f)\" font-size=\"%4.8fpt\">%s</text>\n", | 618 | fprintf(svgfile, "<text transform=\"translate(%.8f, %.8f)\" font-size=\"%.8fpt\">%s</text>\n", |
| 543 | X + boxsize + 5, boxsize, 0.8 * boxsize, text); | 619 | X + boxsize + 5, boxsize, 0.8 * boxsize, text); |
| 544 | } | 620 | } |
| 545 | 621 | ||
| 622 | void svg_io_legenda(void) | ||
| 623 | { | ||
| 624 | if (!svgfile) | ||
| 625 | return; | ||
| 626 | |||
| 627 | fprintf(svgfile, "<g>\n"); | ||
| 628 | svg_legenda_box(0, "Disk", "disk"); | ||
| 629 | svg_legenda_box(100, "Network", "net"); | ||
| 630 | svg_legenda_box(200, "Sync", "sync"); | ||
| 631 | svg_legenda_box(300, "Poll", "poll"); | ||
| 632 | svg_legenda_box(400, "Error", "error"); | ||
| 633 | fprintf(svgfile, "</g>\n"); | ||
| 634 | } | ||
| 635 | |||
| 546 | void svg_legenda(void) | 636 | void svg_legenda(void) |
| 547 | { | 637 | { |
| 548 | if (!svgfile) | 638 | if (!svgfile) |
| @@ -559,7 +649,7 @@ void svg_legenda(void) | |||
| 559 | fprintf(svgfile, "</g>\n"); | 649 | fprintf(svgfile, "</g>\n"); |
| 560 | } | 650 | } |
| 561 | 651 | ||
| 562 | void svg_time_grid(void) | 652 | void svg_time_grid(double min_thickness) |
| 563 | { | 653 | { |
| 564 | u64 i; | 654 | u64 i; |
| 565 | 655 | ||
| @@ -579,8 +669,10 @@ void svg_time_grid(void) | |||
| 579 | color = 128; | 669 | color = 128; |
| 580 | } | 670 | } |
| 581 | 671 | ||
| 582 | fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%" PRIu64 "\" style=\"stroke:rgb(%i,%i,%i);stroke-width:%1.3f\"/>\n", | 672 | if (thickness >= min_thickness) |
| 583 | time2pixels(i), SLOT_MULT/2, time2pixels(i), total_height, color, color, color, thickness); | 673 | fprintf(svgfile, "<line x1=\"%.8f\" y1=\"%.2f\" x2=\"%.8f\" y2=\"%" PRIu64 "\" style=\"stroke:rgb(%i,%i,%i);stroke-width:%.3f\"/>\n", |
| 674 | time2pixels(i), SLOT_MULT/2, time2pixels(i), | ||
| 675 | total_height, color, color, color, thickness); | ||
| 584 | 676 | ||
| 585 | i += 10000000; | 677 | i += 10000000; |
| 586 | } | 678 | } |
diff --git a/tools/perf/util/svghelper.h b/tools/perf/util/svghelper.h index e3aff5332e30..9292a5291445 100644 --- a/tools/perf/util/svghelper.h +++ b/tools/perf/util/svghelper.h | |||
| @@ -4,6 +4,9 @@ | |||
| 4 | #include <linux/types.h> | 4 | #include <linux/types.h> |
| 5 | 5 | ||
| 6 | extern void open_svg(const char *filename, int cpus, int rows, u64 start, u64 end); | 6 | extern void open_svg(const char *filename, int cpus, int rows, u64 start, u64 end); |
| 7 | extern void svg_ubox(int Yslot, u64 start, u64 end, double height, const char *type, int fd, int err, int merges); | ||
| 8 | extern void svg_lbox(int Yslot, u64 start, u64 end, double height, const char *type, int fd, int err, int merges); | ||
| 9 | extern void svg_fbox(int Yslot, u64 start, u64 end, double height, const char *type, int fd, int err, int merges); | ||
| 7 | extern void svg_box(int Yslot, u64 start, u64 end, const char *type); | 10 | extern void svg_box(int Yslot, u64 start, u64 end, const char *type); |
| 8 | extern void svg_blocked(int Yslot, int cpu, u64 start, u64 end, const char *backtrace); | 11 | extern void svg_blocked(int Yslot, int cpu, u64 start, u64 end, const char *backtrace); |
| 9 | extern void svg_running(int Yslot, int cpu, u64 start, u64 end, const char *backtrace); | 12 | extern void svg_running(int Yslot, int cpu, u64 start, u64 end, const char *backtrace); |
| @@ -16,7 +19,8 @@ extern void svg_cstate(int cpu, u64 start, u64 end, int type); | |||
| 16 | extern void svg_pstate(int cpu, u64 start, u64 end, u64 freq); | 19 | extern void svg_pstate(int cpu, u64 start, u64 end, u64 freq); |
| 17 | 20 | ||
| 18 | 21 | ||
| 19 | extern void svg_time_grid(void); | 22 | extern void svg_time_grid(double min_thickness); |
| 23 | extern void svg_io_legenda(void); | ||
| 20 | extern void svg_legenda(void); | 24 | extern void svg_legenda(void); |
| 21 | extern void svg_wakeline(u64 start, int row1, int row2, const char *backtrace); | 25 | extern void svg_wakeline(u64 start, int row1, int row2, const char *backtrace); |
| 22 | extern void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc2, const char *backtrace); | 26 | extern void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc2, const char *backtrace); |
