diff options
Diffstat (limited to 'tools/perf/builtin-stat.c')
-rw-r--r-- | tools/perf/builtin-stat.c | 567 |
1 files changed, 463 insertions, 104 deletions
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c index 038e877081b6..1f19f2f999c8 100644 --- a/tools/perf/builtin-stat.c +++ b/tools/perf/builtin-stat.c | |||
@@ -122,6 +122,7 @@ static bool sync_run = false; | |||
122 | static unsigned int initial_delay = 0; | 122 | static unsigned int initial_delay = 0; |
123 | static unsigned int unit_width = 4; /* strlen("unit") */ | 123 | static unsigned int unit_width = 4; /* strlen("unit") */ |
124 | static bool forever = false; | 124 | static bool forever = false; |
125 | static bool metric_only = false; | ||
125 | static struct timespec ref_time; | 126 | static struct timespec ref_time; |
126 | static struct cpu_map *aggr_map; | 127 | static struct cpu_map *aggr_map; |
127 | static aggr_get_id_t aggr_get_id; | 128 | static aggr_get_id_t aggr_get_id; |
@@ -735,6 +736,191 @@ static void aggr_printout(struct perf_evsel *evsel, int id, int nr) | |||
735 | } | 736 | } |
736 | } | 737 | } |
737 | 738 | ||
739 | struct outstate { | ||
740 | FILE *fh; | ||
741 | bool newline; | ||
742 | const char *prefix; | ||
743 | int nfields; | ||
744 | int id, nr; | ||
745 | struct perf_evsel *evsel; | ||
746 | }; | ||
747 | |||
748 | #define METRIC_LEN 35 | ||
749 | |||
750 | static void new_line_std(void *ctx) | ||
751 | { | ||
752 | struct outstate *os = ctx; | ||
753 | |||
754 | os->newline = true; | ||
755 | } | ||
756 | |||
757 | static void do_new_line_std(struct outstate *os) | ||
758 | { | ||
759 | fputc('\n', os->fh); | ||
760 | fputs(os->prefix, os->fh); | ||
761 | aggr_printout(os->evsel, os->id, os->nr); | ||
762 | if (stat_config.aggr_mode == AGGR_NONE) | ||
763 | fprintf(os->fh, " "); | ||
764 | fprintf(os->fh, " "); | ||
765 | } | ||
766 | |||
767 | static void print_metric_std(void *ctx, const char *color, const char *fmt, | ||
768 | const char *unit, double val) | ||
769 | { | ||
770 | struct outstate *os = ctx; | ||
771 | FILE *out = os->fh; | ||
772 | int n; | ||
773 | bool newline = os->newline; | ||
774 | |||
775 | os->newline = false; | ||
776 | |||
777 | if (unit == NULL || fmt == NULL) { | ||
778 | fprintf(out, "%-*s", METRIC_LEN, ""); | ||
779 | return; | ||
780 | } | ||
781 | |||
782 | if (newline) | ||
783 | do_new_line_std(os); | ||
784 | |||
785 | n = fprintf(out, " # "); | ||
786 | if (color) | ||
787 | n += color_fprintf(out, color, fmt, val); | ||
788 | else | ||
789 | n += fprintf(out, fmt, val); | ||
790 | fprintf(out, " %-*s", METRIC_LEN - n - 1, unit); | ||
791 | } | ||
792 | |||
793 | static void new_line_csv(void *ctx) | ||
794 | { | ||
795 | struct outstate *os = ctx; | ||
796 | int i; | ||
797 | |||
798 | fputc('\n', os->fh); | ||
799 | if (os->prefix) | ||
800 | fprintf(os->fh, "%s%s", os->prefix, csv_sep); | ||
801 | aggr_printout(os->evsel, os->id, os->nr); | ||
802 | for (i = 0; i < os->nfields; i++) | ||
803 | fputs(csv_sep, os->fh); | ||
804 | } | ||
805 | |||
806 | static void print_metric_csv(void *ctx, | ||
807 | const char *color __maybe_unused, | ||
808 | const char *fmt, const char *unit, double val) | ||
809 | { | ||
810 | struct outstate *os = ctx; | ||
811 | FILE *out = os->fh; | ||
812 | char buf[64], *vals, *ends; | ||
813 | |||
814 | if (unit == NULL || fmt == NULL) { | ||
815 | fprintf(out, "%s%s%s%s", csv_sep, csv_sep, csv_sep, csv_sep); | ||
816 | return; | ||
817 | } | ||
818 | snprintf(buf, sizeof(buf), fmt, val); | ||
819 | vals = buf; | ||
820 | while (isspace(*vals)) | ||
821 | vals++; | ||
822 | ends = vals; | ||
823 | while (isdigit(*ends) || *ends == '.') | ||
824 | ends++; | ||
825 | *ends = 0; | ||
826 | while (isspace(*unit)) | ||
827 | unit++; | ||
828 | fprintf(out, "%s%s%s%s", csv_sep, vals, csv_sep, unit); | ||
829 | } | ||
830 | |||
831 | #define METRIC_ONLY_LEN 20 | ||
832 | |||
833 | /* Filter out some columns that don't work well in metrics only mode */ | ||
834 | |||
835 | static bool valid_only_metric(const char *unit) | ||
836 | { | ||
837 | if (!unit) | ||
838 | return false; | ||
839 | if (strstr(unit, "/sec") || | ||
840 | strstr(unit, "hz") || | ||
841 | strstr(unit, "Hz") || | ||
842 | strstr(unit, "CPUs utilized")) | ||
843 | return false; | ||
844 | return true; | ||
845 | } | ||
846 | |||
847 | static const char *fixunit(char *buf, struct perf_evsel *evsel, | ||
848 | const char *unit) | ||
849 | { | ||
850 | if (!strncmp(unit, "of all", 6)) { | ||
851 | snprintf(buf, 1024, "%s %s", perf_evsel__name(evsel), | ||
852 | unit); | ||
853 | return buf; | ||
854 | } | ||
855 | return unit; | ||
856 | } | ||
857 | |||
858 | static void print_metric_only(void *ctx, const char *color, const char *fmt, | ||
859 | const char *unit, double val) | ||
860 | { | ||
861 | struct outstate *os = ctx; | ||
862 | FILE *out = os->fh; | ||
863 | int n; | ||
864 | char buf[1024]; | ||
865 | unsigned mlen = METRIC_ONLY_LEN; | ||
866 | |||
867 | if (!valid_only_metric(unit)) | ||
868 | return; | ||
869 | unit = fixunit(buf, os->evsel, unit); | ||
870 | if (color) | ||
871 | n = color_fprintf(out, color, fmt, val); | ||
872 | else | ||
873 | n = fprintf(out, fmt, val); | ||
874 | if (n > METRIC_ONLY_LEN) | ||
875 | n = METRIC_ONLY_LEN; | ||
876 | if (mlen < strlen(unit)) | ||
877 | mlen = strlen(unit) + 1; | ||
878 | fprintf(out, "%*s", mlen - n, ""); | ||
879 | } | ||
880 | |||
881 | static void print_metric_only_csv(void *ctx, const char *color __maybe_unused, | ||
882 | const char *fmt, | ||
883 | const char *unit, double val) | ||
884 | { | ||
885 | struct outstate *os = ctx; | ||
886 | FILE *out = os->fh; | ||
887 | char buf[64], *vals, *ends; | ||
888 | char tbuf[1024]; | ||
889 | |||
890 | if (!valid_only_metric(unit)) | ||
891 | return; | ||
892 | unit = fixunit(tbuf, os->evsel, unit); | ||
893 | snprintf(buf, sizeof buf, fmt, val); | ||
894 | vals = buf; | ||
895 | while (isspace(*vals)) | ||
896 | vals++; | ||
897 | ends = vals; | ||
898 | while (isdigit(*ends) || *ends == '.') | ||
899 | ends++; | ||
900 | *ends = 0; | ||
901 | fprintf(out, "%s%s", vals, csv_sep); | ||
902 | } | ||
903 | |||
904 | static void new_line_metric(void *ctx __maybe_unused) | ||
905 | { | ||
906 | } | ||
907 | |||
908 | static void print_metric_header(void *ctx, const char *color __maybe_unused, | ||
909 | const char *fmt __maybe_unused, | ||
910 | const char *unit, double val __maybe_unused) | ||
911 | { | ||
912 | struct outstate *os = ctx; | ||
913 | char tbuf[1024]; | ||
914 | |||
915 | if (!valid_only_metric(unit)) | ||
916 | return; | ||
917 | unit = fixunit(tbuf, os->evsel, unit); | ||
918 | if (csv_output) | ||
919 | fprintf(os->fh, "%s%s", unit, csv_sep); | ||
920 | else | ||
921 | fprintf(os->fh, "%-*s ", METRIC_ONLY_LEN, unit); | ||
922 | } | ||
923 | |||
738 | static void nsec_printout(int id, int nr, struct perf_evsel *evsel, double avg) | 924 | static void nsec_printout(int id, int nr, struct perf_evsel *evsel, double avg) |
739 | { | 925 | { |
740 | FILE *output = stat_config.output; | 926 | FILE *output = stat_config.output; |
@@ -763,6 +949,28 @@ static void nsec_printout(int id, int nr, struct perf_evsel *evsel, double avg) | |||
763 | fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); | 949 | fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); |
764 | } | 950 | } |
765 | 951 | ||
952 | static int first_shadow_cpu(struct perf_evsel *evsel, int id) | ||
953 | { | ||
954 | int i; | ||
955 | |||
956 | if (!aggr_get_id) | ||
957 | return 0; | ||
958 | |||
959 | if (stat_config.aggr_mode == AGGR_NONE) | ||
960 | return id; | ||
961 | |||
962 | if (stat_config.aggr_mode == AGGR_GLOBAL) | ||
963 | return 0; | ||
964 | |||
965 | for (i = 0; i < perf_evsel__nr_cpus(evsel); i++) { | ||
966 | int cpu2 = perf_evsel__cpus(evsel)->map[i]; | ||
967 | |||
968 | if (aggr_get_id(evsel_list->cpus, cpu2) == id) | ||
969 | return cpu2; | ||
970 | } | ||
971 | return 0; | ||
972 | } | ||
973 | |||
766 | static void abs_printout(int id, int nr, struct perf_evsel *evsel, double avg) | 974 | static void abs_printout(int id, int nr, struct perf_evsel *evsel, double avg) |
767 | { | 975 | { |
768 | FILE *output = stat_config.output; | 976 | FILE *output = stat_config.output; |
@@ -793,22 +1001,124 @@ static void abs_printout(int id, int nr, struct perf_evsel *evsel, double avg) | |||
793 | fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); | 1001 | fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); |
794 | } | 1002 | } |
795 | 1003 | ||
796 | static void printout(int id, int nr, struct perf_evsel *counter, double uval) | 1004 | static void printout(int id, int nr, struct perf_evsel *counter, double uval, |
1005 | char *prefix, u64 run, u64 ena, double noise) | ||
797 | { | 1006 | { |
798 | int cpu = cpu_map__id_to_cpu(id); | 1007 | struct perf_stat_output_ctx out; |
1008 | struct outstate os = { | ||
1009 | .fh = stat_config.output, | ||
1010 | .prefix = prefix ? prefix : "", | ||
1011 | .id = id, | ||
1012 | .nr = nr, | ||
1013 | .evsel = counter, | ||
1014 | }; | ||
1015 | print_metric_t pm = print_metric_std; | ||
1016 | void (*nl)(void *); | ||
799 | 1017 | ||
800 | if (stat_config.aggr_mode == AGGR_GLOBAL) | 1018 | if (metric_only) { |
801 | cpu = 0; | 1019 | nl = new_line_metric; |
1020 | if (csv_output) | ||
1021 | pm = print_metric_only_csv; | ||
1022 | else | ||
1023 | pm = print_metric_only; | ||
1024 | } else | ||
1025 | nl = new_line_std; | ||
1026 | |||
1027 | if (csv_output && !metric_only) { | ||
1028 | static int aggr_fields[] = { | ||
1029 | [AGGR_GLOBAL] = 0, | ||
1030 | [AGGR_THREAD] = 1, | ||
1031 | [AGGR_NONE] = 1, | ||
1032 | [AGGR_SOCKET] = 2, | ||
1033 | [AGGR_CORE] = 2, | ||
1034 | }; | ||
1035 | |||
1036 | pm = print_metric_csv; | ||
1037 | nl = new_line_csv; | ||
1038 | os.nfields = 3; | ||
1039 | os.nfields += aggr_fields[stat_config.aggr_mode]; | ||
1040 | if (counter->cgrp) | ||
1041 | os.nfields++; | ||
1042 | } | ||
1043 | if (run == 0 || ena == 0 || counter->counts->scaled == -1) { | ||
1044 | if (metric_only) { | ||
1045 | pm(&os, NULL, "", "", 0); | ||
1046 | return; | ||
1047 | } | ||
1048 | aggr_printout(counter, id, nr); | ||
1049 | |||
1050 | fprintf(stat_config.output, "%*s%s", | ||
1051 | csv_output ? 0 : 18, | ||
1052 | counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED, | ||
1053 | csv_sep); | ||
1054 | |||
1055 | fprintf(stat_config.output, "%-*s%s", | ||
1056 | csv_output ? 0 : unit_width, | ||
1057 | counter->unit, csv_sep); | ||
1058 | |||
1059 | fprintf(stat_config.output, "%*s", | ||
1060 | csv_output ? 0 : -25, | ||
1061 | perf_evsel__name(counter)); | ||
1062 | |||
1063 | if (counter->cgrp) | ||
1064 | fprintf(stat_config.output, "%s%s", | ||
1065 | csv_sep, counter->cgrp->name); | ||
802 | 1066 | ||
803 | if (nsec_counter(counter)) | 1067 | if (!csv_output) |
1068 | pm(&os, NULL, NULL, "", 0); | ||
1069 | print_noise(counter, noise); | ||
1070 | print_running(run, ena); | ||
1071 | if (csv_output) | ||
1072 | pm(&os, NULL, NULL, "", 0); | ||
1073 | return; | ||
1074 | } | ||
1075 | |||
1076 | if (metric_only) | ||
1077 | /* nothing */; | ||
1078 | else if (nsec_counter(counter)) | ||
804 | nsec_printout(id, nr, counter, uval); | 1079 | nsec_printout(id, nr, counter, uval); |
805 | else | 1080 | else |
806 | abs_printout(id, nr, counter, uval); | 1081 | abs_printout(id, nr, counter, uval); |
807 | 1082 | ||
808 | if (!csv_output && !stat_config.interval) | 1083 | out.print_metric = pm; |
809 | perf_stat__print_shadow_stats(stat_config.output, counter, | 1084 | out.new_line = nl; |
810 | uval, cpu, | 1085 | out.ctx = &os; |
811 | stat_config.aggr_mode); | 1086 | |
1087 | if (csv_output && !metric_only) { | ||
1088 | print_noise(counter, noise); | ||
1089 | print_running(run, ena); | ||
1090 | } | ||
1091 | |||
1092 | perf_stat__print_shadow_stats(counter, uval, | ||
1093 | first_shadow_cpu(counter, id), | ||
1094 | &out); | ||
1095 | if (!csv_output && !metric_only) { | ||
1096 | print_noise(counter, noise); | ||
1097 | print_running(run, ena); | ||
1098 | } | ||
1099 | } | ||
1100 | |||
1101 | static void aggr_update_shadow(void) | ||
1102 | { | ||
1103 | int cpu, s2, id, s; | ||
1104 | u64 val; | ||
1105 | struct perf_evsel *counter; | ||
1106 | |||
1107 | for (s = 0; s < aggr_map->nr; s++) { | ||
1108 | id = aggr_map->map[s]; | ||
1109 | evlist__for_each(evsel_list, counter) { | ||
1110 | val = 0; | ||
1111 | for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) { | ||
1112 | s2 = aggr_get_id(evsel_list->cpus, cpu); | ||
1113 | if (s2 != id) | ||
1114 | continue; | ||
1115 | val += perf_counts(counter->counts, cpu, 0)->val; | ||
1116 | } | ||
1117 | val = val * counter->scale; | ||
1118 | perf_stat__update_shadow_stats(counter, &val, | ||
1119 | first_shadow_cpu(counter, id)); | ||
1120 | } | ||
1121 | } | ||
812 | } | 1122 | } |
813 | 1123 | ||
814 | static void print_aggr(char *prefix) | 1124 | static void print_aggr(char *prefix) |
@@ -818,12 +1128,23 @@ static void print_aggr(char *prefix) | |||
818 | int cpu, s, s2, id, nr; | 1128 | int cpu, s, s2, id, nr; |
819 | double uval; | 1129 | double uval; |
820 | u64 ena, run, val; | 1130 | u64 ena, run, val; |
1131 | bool first; | ||
821 | 1132 | ||
822 | if (!(aggr_map || aggr_get_id)) | 1133 | if (!(aggr_map || aggr_get_id)) |
823 | return; | 1134 | return; |
824 | 1135 | ||
1136 | aggr_update_shadow(); | ||
1137 | |||
1138 | /* | ||
1139 | * With metric_only everything is on a single line. | ||
1140 | * Without each counter has its own line. | ||
1141 | */ | ||
825 | for (s = 0; s < aggr_map->nr; s++) { | 1142 | for (s = 0; s < aggr_map->nr; s++) { |
1143 | if (prefix && metric_only) | ||
1144 | fprintf(output, "%s", prefix); | ||
1145 | |||
826 | id = aggr_map->map[s]; | 1146 | id = aggr_map->map[s]; |
1147 | first = true; | ||
827 | evlist__for_each(evsel_list, counter) { | 1148 | evlist__for_each(evsel_list, counter) { |
828 | val = ena = run = 0; | 1149 | val = ena = run = 0; |
829 | nr = 0; | 1150 | nr = 0; |
@@ -836,41 +1157,20 @@ static void print_aggr(char *prefix) | |||
836 | run += perf_counts(counter->counts, cpu, 0)->run; | 1157 | run += perf_counts(counter->counts, cpu, 0)->run; |
837 | nr++; | 1158 | nr++; |
838 | } | 1159 | } |
839 | if (prefix) | 1160 | if (first && metric_only) { |
840 | fprintf(output, "%s", prefix); | 1161 | first = false; |
841 | |||
842 | if (run == 0 || ena == 0) { | ||
843 | aggr_printout(counter, id, nr); | 1162 | aggr_printout(counter, id, nr); |
844 | |||
845 | fprintf(output, "%*s%s", | ||
846 | csv_output ? 0 : 18, | ||
847 | counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED, | ||
848 | csv_sep); | ||
849 | |||
850 | fprintf(output, "%-*s%s", | ||
851 | csv_output ? 0 : unit_width, | ||
852 | counter->unit, csv_sep); | ||
853 | |||
854 | fprintf(output, "%*s", | ||
855 | csv_output ? 0 : -25, | ||
856 | perf_evsel__name(counter)); | ||
857 | |||
858 | if (counter->cgrp) | ||
859 | fprintf(output, "%s%s", | ||
860 | csv_sep, counter->cgrp->name); | ||
861 | |||
862 | print_running(run, ena); | ||
863 | fputc('\n', output); | ||
864 | continue; | ||
865 | } | 1163 | } |
866 | uval = val * counter->scale; | 1164 | if (prefix && !metric_only) |
867 | printout(id, nr, counter, uval); | 1165 | fprintf(output, "%s", prefix); |
868 | if (!csv_output) | ||
869 | print_noise(counter, 1.0); | ||
870 | 1166 | ||
871 | print_running(run, ena); | 1167 | uval = val * counter->scale; |
872 | fputc('\n', output); | 1168 | printout(id, nr, counter, uval, prefix, run, ena, 1.0); |
1169 | if (!metric_only) | ||
1170 | fputc('\n', output); | ||
873 | } | 1171 | } |
1172 | if (metric_only) | ||
1173 | fputc('\n', output); | ||
874 | } | 1174 | } |
875 | } | 1175 | } |
876 | 1176 | ||
@@ -895,12 +1195,7 @@ static void print_aggr_thread(struct perf_evsel *counter, char *prefix) | |||
895 | fprintf(output, "%s", prefix); | 1195 | fprintf(output, "%s", prefix); |
896 | 1196 | ||
897 | uval = val * counter->scale; | 1197 | uval = val * counter->scale; |
898 | printout(thread, 0, counter, uval); | 1198 | printout(thread, 0, counter, uval, prefix, run, ena, 1.0); |
899 | |||
900 | if (!csv_output) | ||
901 | print_noise(counter, 1.0); | ||
902 | |||
903 | print_running(run, ena); | ||
904 | fputc('\n', output); | 1199 | fputc('\n', output); |
905 | } | 1200 | } |
906 | } | 1201 | } |
@@ -914,43 +1209,19 @@ static void print_counter_aggr(struct perf_evsel *counter, char *prefix) | |||
914 | FILE *output = stat_config.output; | 1209 | FILE *output = stat_config.output; |
915 | struct perf_stat_evsel *ps = counter->priv; | 1210 | struct perf_stat_evsel *ps = counter->priv; |
916 | double avg = avg_stats(&ps->res_stats[0]); | 1211 | double avg = avg_stats(&ps->res_stats[0]); |
917 | int scaled = counter->counts->scaled; | ||
918 | double uval; | 1212 | double uval; |
919 | double avg_enabled, avg_running; | 1213 | double avg_enabled, avg_running; |
920 | 1214 | ||
921 | avg_enabled = avg_stats(&ps->res_stats[1]); | 1215 | avg_enabled = avg_stats(&ps->res_stats[1]); |
922 | avg_running = avg_stats(&ps->res_stats[2]); | 1216 | avg_running = avg_stats(&ps->res_stats[2]); |
923 | 1217 | ||
924 | if (prefix) | 1218 | if (prefix && !metric_only) |
925 | fprintf(output, "%s", prefix); | 1219 | fprintf(output, "%s", prefix); |
926 | 1220 | ||
927 | if (scaled == -1 || !counter->supported) { | ||
928 | fprintf(output, "%*s%s", | ||
929 | csv_output ? 0 : 18, | ||
930 | counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED, | ||
931 | csv_sep); | ||
932 | fprintf(output, "%-*s%s", | ||
933 | csv_output ? 0 : unit_width, | ||
934 | counter->unit, csv_sep); | ||
935 | fprintf(output, "%*s", | ||
936 | csv_output ? 0 : -25, | ||
937 | perf_evsel__name(counter)); | ||
938 | |||
939 | if (counter->cgrp) | ||
940 | fprintf(output, "%s%s", csv_sep, counter->cgrp->name); | ||
941 | |||
942 | print_running(avg_running, avg_enabled); | ||
943 | fputc('\n', output); | ||
944 | return; | ||
945 | } | ||
946 | |||
947 | uval = avg * counter->scale; | 1221 | uval = avg * counter->scale; |
948 | printout(-1, 0, counter, uval); | 1222 | printout(-1, 0, counter, uval, prefix, avg_running, avg_enabled, avg); |
949 | 1223 | if (!metric_only) | |
950 | print_noise(counter, avg); | 1224 | fprintf(output, "\n"); |
951 | |||
952 | print_running(avg_running, avg_enabled); | ||
953 | fprintf(output, "\n"); | ||
954 | } | 1225 | } |
955 | 1226 | ||
956 | /* | 1227 | /* |
@@ -972,39 +1243,78 @@ static void print_counter(struct perf_evsel *counter, char *prefix) | |||
972 | if (prefix) | 1243 | if (prefix) |
973 | fprintf(output, "%s", prefix); | 1244 | fprintf(output, "%s", prefix); |
974 | 1245 | ||
975 | if (run == 0 || ena == 0) { | 1246 | uval = val * counter->scale; |
976 | fprintf(output, "CPU%*d%s%*s%s", | 1247 | printout(cpu, 0, counter, uval, prefix, run, ena, 1.0); |
977 | csv_output ? 0 : -4, | ||
978 | perf_evsel__cpus(counter)->map[cpu], csv_sep, | ||
979 | csv_output ? 0 : 18, | ||
980 | counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED, | ||
981 | csv_sep); | ||
982 | 1248 | ||
983 | fprintf(output, "%-*s%s", | 1249 | fputc('\n', output); |
984 | csv_output ? 0 : unit_width, | 1250 | } |
985 | counter->unit, csv_sep); | 1251 | } |
986 | 1252 | ||
987 | fprintf(output, "%*s", | 1253 | static void print_no_aggr_metric(char *prefix) |
988 | csv_output ? 0 : -25, | 1254 | { |
989 | perf_evsel__name(counter)); | 1255 | int cpu; |
1256 | int nrcpus = 0; | ||
1257 | struct perf_evsel *counter; | ||
1258 | u64 ena, run, val; | ||
1259 | double uval; | ||
990 | 1260 | ||
991 | if (counter->cgrp) | 1261 | nrcpus = evsel_list->cpus->nr; |
992 | fprintf(output, "%s%s", | 1262 | for (cpu = 0; cpu < nrcpus; cpu++) { |
993 | csv_sep, counter->cgrp->name); | 1263 | bool first = true; |
994 | 1264 | ||
995 | print_running(run, ena); | 1265 | if (prefix) |
996 | fputc('\n', output); | 1266 | fputs(prefix, stat_config.output); |
997 | continue; | 1267 | evlist__for_each(evsel_list, counter) { |
1268 | if (first) { | ||
1269 | aggr_printout(counter, cpu, 0); | ||
1270 | first = false; | ||
1271 | } | ||
1272 | val = perf_counts(counter->counts, cpu, 0)->val; | ||
1273 | ena = perf_counts(counter->counts, cpu, 0)->ena; | ||
1274 | run = perf_counts(counter->counts, cpu, 0)->run; | ||
1275 | |||
1276 | uval = val * counter->scale; | ||
1277 | printout(cpu, 0, counter, uval, prefix, run, ena, 1.0); | ||
998 | } | 1278 | } |
1279 | fputc('\n', stat_config.output); | ||
1280 | } | ||
1281 | } | ||
999 | 1282 | ||
1000 | uval = val * counter->scale; | 1283 | static int aggr_header_lens[] = { |
1001 | printout(cpu, 0, counter, uval); | 1284 | [AGGR_CORE] = 18, |
1002 | if (!csv_output) | 1285 | [AGGR_SOCKET] = 12, |
1003 | print_noise(counter, 1.0); | 1286 | [AGGR_NONE] = 6, |
1004 | print_running(run, ena); | 1287 | [AGGR_THREAD] = 24, |
1288 | [AGGR_GLOBAL] = 0, | ||
1289 | }; | ||
1005 | 1290 | ||
1006 | fputc('\n', output); | 1291 | static void print_metric_headers(char *prefix) |
1292 | { | ||
1293 | struct perf_stat_output_ctx out; | ||
1294 | struct perf_evsel *counter; | ||
1295 | struct outstate os = { | ||
1296 | .fh = stat_config.output | ||
1297 | }; | ||
1298 | |||
1299 | if (prefix) | ||
1300 | fprintf(stat_config.output, "%s", prefix); | ||
1301 | |||
1302 | if (!csv_output) | ||
1303 | fprintf(stat_config.output, "%*s", | ||
1304 | aggr_header_lens[stat_config.aggr_mode], ""); | ||
1305 | |||
1306 | /* Print metrics headers only */ | ||
1307 | evlist__for_each(evsel_list, counter) { | ||
1308 | os.evsel = counter; | ||
1309 | out.ctx = &os; | ||
1310 | out.print_metric = print_metric_header; | ||
1311 | out.new_line = new_line_metric; | ||
1312 | os.evsel = counter; | ||
1313 | perf_stat__print_shadow_stats(counter, 0, | ||
1314 | 0, | ||
1315 | &out); | ||
1007 | } | 1316 | } |
1317 | fputc('\n', stat_config.output); | ||
1008 | } | 1318 | } |
1009 | 1319 | ||
1010 | static void print_interval(char *prefix, struct timespec *ts) | 1320 | static void print_interval(char *prefix, struct timespec *ts) |
@@ -1014,7 +1324,7 @@ static void print_interval(char *prefix, struct timespec *ts) | |||
1014 | 1324 | ||
1015 | sprintf(prefix, "%6lu.%09lu%s", ts->tv_sec, ts->tv_nsec, csv_sep); | 1325 | sprintf(prefix, "%6lu.%09lu%s", ts->tv_sec, ts->tv_nsec, csv_sep); |
1016 | 1326 | ||
1017 | if (num_print_interval == 0 && !csv_output) { | 1327 | if (num_print_interval == 0 && !csv_output && !metric_only) { |
1018 | switch (stat_config.aggr_mode) { | 1328 | switch (stat_config.aggr_mode) { |
1019 | case AGGR_SOCKET: | 1329 | case AGGR_SOCKET: |
1020 | fprintf(output, "# time socket cpus counts %*s events\n", unit_width, "unit"); | 1330 | fprintf(output, "# time socket cpus counts %*s events\n", unit_width, "unit"); |
@@ -1101,6 +1411,17 @@ static void print_counters(struct timespec *ts, int argc, const char **argv) | |||
1101 | else | 1411 | else |
1102 | print_header(argc, argv); | 1412 | print_header(argc, argv); |
1103 | 1413 | ||
1414 | if (metric_only) { | ||
1415 | static int num_print_iv; | ||
1416 | |||
1417 | if (num_print_iv == 0) | ||
1418 | print_metric_headers(prefix); | ||
1419 | if (num_print_iv++ == 25) | ||
1420 | num_print_iv = 0; | ||
1421 | if (stat_config.aggr_mode == AGGR_GLOBAL && prefix) | ||
1422 | fprintf(stat_config.output, "%s", prefix); | ||
1423 | } | ||
1424 | |||
1104 | switch (stat_config.aggr_mode) { | 1425 | switch (stat_config.aggr_mode) { |
1105 | case AGGR_CORE: | 1426 | case AGGR_CORE: |
1106 | case AGGR_SOCKET: | 1427 | case AGGR_SOCKET: |
@@ -1113,10 +1434,16 @@ static void print_counters(struct timespec *ts, int argc, const char **argv) | |||
1113 | case AGGR_GLOBAL: | 1434 | case AGGR_GLOBAL: |
1114 | evlist__for_each(evsel_list, counter) | 1435 | evlist__for_each(evsel_list, counter) |
1115 | print_counter_aggr(counter, prefix); | 1436 | print_counter_aggr(counter, prefix); |
1437 | if (metric_only) | ||
1438 | fputc('\n', stat_config.output); | ||
1116 | break; | 1439 | break; |
1117 | case AGGR_NONE: | 1440 | case AGGR_NONE: |
1118 | evlist__for_each(evsel_list, counter) | 1441 | if (metric_only) |
1119 | print_counter(counter, prefix); | 1442 | print_no_aggr_metric(prefix); |
1443 | else { | ||
1444 | evlist__for_each(evsel_list, counter) | ||
1445 | print_counter(counter, prefix); | ||
1446 | } | ||
1120 | break; | 1447 | break; |
1121 | case AGGR_UNSET: | 1448 | case AGGR_UNSET: |
1122 | default: | 1449 | default: |
@@ -1237,6 +1564,8 @@ static const struct option stat_options[] = { | |||
1237 | "aggregate counts per thread", AGGR_THREAD), | 1564 | "aggregate counts per thread", AGGR_THREAD), |
1238 | OPT_UINTEGER('D', "delay", &initial_delay, | 1565 | OPT_UINTEGER('D', "delay", &initial_delay, |
1239 | "ms to wait before starting measurement after program start"), | 1566 | "ms to wait before starting measurement after program start"), |
1567 | OPT_BOOLEAN(0, "metric-only", &metric_only, | ||
1568 | "Only print computed metrics. No raw values"), | ||
1240 | OPT_END() | 1569 | OPT_END() |
1241 | }; | 1570 | }; |
1242 | 1571 | ||
@@ -1435,7 +1764,7 @@ static int perf_stat_init_aggr_mode_file(struct perf_stat *st) | |||
1435 | */ | 1764 | */ |
1436 | static int add_default_attributes(void) | 1765 | static int add_default_attributes(void) |
1437 | { | 1766 | { |
1438 | struct perf_event_attr default_attrs[] = { | 1767 | struct perf_event_attr default_attrs0[] = { |
1439 | 1768 | ||
1440 | { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_TASK_CLOCK }, | 1769 | { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_TASK_CLOCK }, |
1441 | { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_CONTEXT_SWITCHES }, | 1770 | { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_CONTEXT_SWITCHES }, |
@@ -1443,8 +1772,14 @@ static int add_default_attributes(void) | |||
1443 | { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_PAGE_FAULTS }, | 1772 | { .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_PAGE_FAULTS }, |
1444 | 1773 | ||
1445 | { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_CPU_CYCLES }, | 1774 | { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_CPU_CYCLES }, |
1775 | }; | ||
1776 | struct perf_event_attr frontend_attrs[] = { | ||
1446 | { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_STALLED_CYCLES_FRONTEND }, | 1777 | { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_STALLED_CYCLES_FRONTEND }, |
1778 | }; | ||
1779 | struct perf_event_attr backend_attrs[] = { | ||
1447 | { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_STALLED_CYCLES_BACKEND }, | 1780 | { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_STALLED_CYCLES_BACKEND }, |
1781 | }; | ||
1782 | struct perf_event_attr default_attrs1[] = { | ||
1448 | { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_INSTRUCTIONS }, | 1783 | { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_INSTRUCTIONS }, |
1449 | { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS }, | 1784 | { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS }, |
1450 | { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_BRANCH_MISSES }, | 1785 | { .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_BRANCH_MISSES }, |
@@ -1561,7 +1896,19 @@ static int add_default_attributes(void) | |||
1561 | } | 1896 | } |
1562 | 1897 | ||
1563 | if (!evsel_list->nr_entries) { | 1898 | if (!evsel_list->nr_entries) { |
1564 | if (perf_evlist__add_default_attrs(evsel_list, default_attrs) < 0) | 1899 | if (perf_evlist__add_default_attrs(evsel_list, default_attrs0) < 0) |
1900 | return -1; | ||
1901 | if (pmu_have_event("cpu", "stalled-cycles-frontend")) { | ||
1902 | if (perf_evlist__add_default_attrs(evsel_list, | ||
1903 | frontend_attrs) < 0) | ||
1904 | return -1; | ||
1905 | } | ||
1906 | if (pmu_have_event("cpu", "stalled-cycles-backend")) { | ||
1907 | if (perf_evlist__add_default_attrs(evsel_list, | ||
1908 | backend_attrs) < 0) | ||
1909 | return -1; | ||
1910 | } | ||
1911 | if (perf_evlist__add_default_attrs(evsel_list, default_attrs1) < 0) | ||
1565 | return -1; | 1912 | return -1; |
1566 | } | 1913 | } |
1567 | 1914 | ||
@@ -1825,9 +2172,11 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1825 | if (evsel_list == NULL) | 2172 | if (evsel_list == NULL) |
1826 | return -ENOMEM; | 2173 | return -ENOMEM; |
1827 | 2174 | ||
2175 | parse_events__shrink_config_terms(); | ||
1828 | argc = parse_options_subcommand(argc, argv, stat_options, stat_subcommands, | 2176 | argc = parse_options_subcommand(argc, argv, stat_options, stat_subcommands, |
1829 | (const char **) stat_usage, | 2177 | (const char **) stat_usage, |
1830 | PARSE_OPT_STOP_AT_NON_OPTION); | 2178 | PARSE_OPT_STOP_AT_NON_OPTION); |
2179 | perf_stat__init_shadow_stats(); | ||
1831 | 2180 | ||
1832 | if (csv_sep) { | 2181 | if (csv_sep) { |
1833 | csv_output = true; | 2182 | csv_output = true; |
@@ -1858,6 +2207,16 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused) | |||
1858 | goto out; | 2207 | goto out; |
1859 | } | 2208 | } |
1860 | 2209 | ||
2210 | if (metric_only && stat_config.aggr_mode == AGGR_THREAD) { | ||
2211 | fprintf(stderr, "--metric-only is not supported with --per-thread\n"); | ||
2212 | goto out; | ||
2213 | } | ||
2214 | |||
2215 | if (metric_only && run_count > 1) { | ||
2216 | fprintf(stderr, "--metric-only is not supported with -r\n"); | ||
2217 | goto out; | ||
2218 | } | ||
2219 | |||
1861 | if (output_fd < 0) { | 2220 | if (output_fd < 0) { |
1862 | fprintf(stderr, "argument to --log-fd must be a > 0\n"); | 2221 | fprintf(stderr, "argument to --log-fd must be a > 0\n"); |
1863 | parse_options_usage(stat_usage, stat_options, "log-fd", 0); | 2222 | parse_options_usage(stat_usage, stat_options, "log-fd", 0); |