aboutsummaryrefslogtreecommitdiffstats
path: root/tools/perf
diff options
context:
space:
mode:
authorStephane Eranian <eranian@google.com>2013-11-12 11:58:49 -0500
committerIngo Molnar <mingo@kernel.org>2013-11-27 05:16:39 -0500
commit410136f5dd96b6013fe6d1011b523b1c247e1ccb (patch)
treee30c0e866a1879752b570ad3973af5c0ef0e4cdd /tools/perf
parent71ad88efebbcde374bddf904b96f3a7fc82d45d4 (diff)
tools/perf/stat: Add event unit and scale support
This patch adds perf stat support for handling event units and scales as exported by the kernel. The kernel can export PMU events actual unit and scaling factor via sysfs: $ ls -1 /sys/devices/power/events/energy-* /sys/devices/power/events/energy-cores /sys/devices/power/events/energy-cores.scale /sys/devices/power/events/energy-cores.unit /sys/devices/power/events/energy-pkg /sys/devices/power/events/energy-pkg.scale /sys/devices/power/events/energy-pkg.unit $ cat /sys/devices/power/events/energy-cores.scale 2.3283064365386962890625e-10 $ cat cat /sys/devices/power/events/energy-cores.unit Joules This patch modifies the pmu event alias code to check for the presence of the .unit and .scale files to load the corresponding values. They are then used by perf stat transparently: # perf stat -a -e power/energy-pkg/,power/energy-cores/,cycles -I 1000 sleep 1000 # time counts unit events 1.000214717 3.07 Joules power/energy-pkg/ [100.00%] 1.000214717 0.53 Joules power/energy-cores/ 1.000214717 12965028 cycles [100.00%] 2.000749289 3.01 Joules power/energy-pkg/ 2.000749289 0.52 Joules power/energy-cores/ 2.000749289 15817043 cycles When the event does not have an explicit unit exported by the kernel, nothing is printed. In csv output mode, there will be an empty field. Special thanks to Jiri for providing the supporting code in the parser to trigger reading of the scale and unit files. Signed-off-by: Stephane Eranian <eranian@google.com> Reviewed-by: Jiri Olsa <jolsa@redhat.com> Reviewed-by: Andi Kleen <ak@linux.intel.com> Signed-off-by: Peter Zijlstra <peterz@infradead.org> Cc: zheng.z.yan@intel.com Cc: bp@alien8.de Cc: maria.n.dimakopoulou@gmail.com Cc: acme@redhat.com Link: http://lkml.kernel.org/r/1384275531-10892-3-git-send-email-eranian@google.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'tools/perf')
-rw-r--r--tools/perf/builtin-stat.c114
-rw-r--r--tools/perf/util/evsel.c2
-rw-r--r--tools/perf/util/evsel.h3
-rw-r--r--tools/perf/util/parse-events.c28
-rw-r--r--tools/perf/util/pmu.c138
-rw-r--r--tools/perf/util/pmu.h3
6 files changed, 244 insertions, 44 deletions
diff --git a/tools/perf/builtin-stat.c b/tools/perf/builtin-stat.c
index ee0d565f83e3..dab98b50c9fe 100644
--- a/tools/perf/builtin-stat.c
+++ b/tools/perf/builtin-stat.c
@@ -138,6 +138,7 @@ static const char *post_cmd = NULL;
138static bool sync_run = false; 138static bool sync_run = false;
139static unsigned int interval = 0; 139static unsigned int interval = 0;
140static unsigned int initial_delay = 0; 140static unsigned int initial_delay = 0;
141static unsigned int unit_width = 4; /* strlen("unit") */
141static bool forever = false; 142static bool forever = false;
142static struct timespec ref_time; 143static struct timespec ref_time;
143static struct cpu_map *aggr_map; 144static struct cpu_map *aggr_map;
@@ -461,17 +462,17 @@ static void print_interval(void)
461 if (num_print_interval == 0 && !csv_output) { 462 if (num_print_interval == 0 && !csv_output) {
462 switch (aggr_mode) { 463 switch (aggr_mode) {
463 case AGGR_SOCKET: 464 case AGGR_SOCKET:
464 fprintf(output, "# time socket cpus counts events\n"); 465 fprintf(output, "# time socket cpus counts %*s events\n", unit_width, "unit");
465 break; 466 break;
466 case AGGR_CORE: 467 case AGGR_CORE:
467 fprintf(output, "# time core cpus counts events\n"); 468 fprintf(output, "# time core cpus counts %*s events\n", unit_width, "unit");
468 break; 469 break;
469 case AGGR_NONE: 470 case AGGR_NONE:
470 fprintf(output, "# time CPU counts events\n"); 471 fprintf(output, "# time CPU counts %*s events\n", unit_width, "unit");
471 break; 472 break;
472 case AGGR_GLOBAL: 473 case AGGR_GLOBAL:
473 default: 474 default:
474 fprintf(output, "# time counts events\n"); 475 fprintf(output, "# time counts %*s events\n", unit_width, "unit");
475 } 476 }
476 } 477 }
477 478
@@ -516,6 +517,7 @@ static int __run_perf_stat(int argc, const char **argv)
516 unsigned long long t0, t1; 517 unsigned long long t0, t1;
517 struct perf_evsel *counter; 518 struct perf_evsel *counter;
518 struct timespec ts; 519 struct timespec ts;
520 size_t l;
519 int status = 0; 521 int status = 0;
520 const bool forks = (argc > 0); 522 const bool forks = (argc > 0);
521 523
@@ -565,6 +567,10 @@ static int __run_perf_stat(int argc, const char **argv)
565 return -1; 567 return -1;
566 } 568 }
567 counter->supported = true; 569 counter->supported = true;
570
571 l = strlen(counter->unit);
572 if (l > unit_width)
573 unit_width = l;
568 } 574 }
569 575
570 if (perf_evlist__apply_filters(evsel_list)) { 576 if (perf_evlist__apply_filters(evsel_list)) {
@@ -704,14 +710,25 @@ static void aggr_printout(struct perf_evsel *evsel, int id, int nr)
704static void nsec_printout(int cpu, int nr, struct perf_evsel *evsel, double avg) 710static void nsec_printout(int cpu, int nr, struct perf_evsel *evsel, double avg)
705{ 711{
706 double msecs = avg / 1e6; 712 double msecs = avg / 1e6;
707 const char *fmt = csv_output ? "%.6f%s%s" : "%18.6f%s%-25s"; 713 const char *fmt_v, *fmt_n;
708 char name[25]; 714 char name[25];
709 715
716 fmt_v = csv_output ? "%.6f%s" : "%18.6f%s";
717 fmt_n = csv_output ? "%s" : "%-25s";
718
710 aggr_printout(evsel, cpu, nr); 719 aggr_printout(evsel, cpu, nr);
711 720
712 scnprintf(name, sizeof(name), "%s%s", 721 scnprintf(name, sizeof(name), "%s%s",
713 perf_evsel__name(evsel), csv_output ? "" : " (msec)"); 722 perf_evsel__name(evsel), csv_output ? "" : " (msec)");
714 fprintf(output, fmt, msecs, csv_sep, name); 723
724 fprintf(output, fmt_v, msecs, csv_sep);
725
726 if (csv_output)
727 fprintf(output, "%s%s", evsel->unit, csv_sep);
728 else
729 fprintf(output, "%-*s%s", unit_width, evsel->unit, csv_sep);
730
731 fprintf(output, fmt_n, name);
715 732
716 if (evsel->cgrp) 733 if (evsel->cgrp)
717 fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); 734 fprintf(output, "%s%s", csv_sep, evsel->cgrp->name);
@@ -908,21 +925,31 @@ static void print_ll_cache_misses(int cpu,
908static void abs_printout(int cpu, int nr, struct perf_evsel *evsel, double avg) 925static void abs_printout(int cpu, int nr, struct perf_evsel *evsel, double avg)
909{ 926{
910 double total, ratio = 0.0, total2; 927 double total, ratio = 0.0, total2;
928 double sc = evsel->scale;
911 const char *fmt; 929 const char *fmt;
912 930
913 if (csv_output) 931 if (csv_output) {
914 fmt = "%.0f%s%s"; 932 fmt = sc != 1.0 ? "%.2f%s" : "%.0f%s";
915 else if (big_num) 933 } else {
916 fmt = "%'18.0f%s%-25s"; 934 if (big_num)
917 else 935 fmt = sc != 1.0 ? "%'18.2f%s" : "%'18.0f%s";
918 fmt = "%18.0f%s%-25s"; 936 else
937 fmt = sc != 1.0 ? "%18.2f%s" : "%18.0f%s";
938 }
919 939
920 aggr_printout(evsel, cpu, nr); 940 aggr_printout(evsel, cpu, nr);
921 941
922 if (aggr_mode == AGGR_GLOBAL) 942 if (aggr_mode == AGGR_GLOBAL)
923 cpu = 0; 943 cpu = 0;
924 944
925 fprintf(output, fmt, avg, csv_sep, perf_evsel__name(evsel)); 945 fprintf(output, fmt, avg, csv_sep);
946
947 if (evsel->unit)
948 fprintf(output, "%-*s%s",
949 csv_output ? 0 : unit_width,
950 evsel->unit, csv_sep);
951
952 fprintf(output, "%-*s", csv_output ? 0 : 25, perf_evsel__name(evsel));
926 953
927 if (evsel->cgrp) 954 if (evsel->cgrp)
928 fprintf(output, "%s%s", csv_sep, evsel->cgrp->name); 955 fprintf(output, "%s%s", csv_sep, evsel->cgrp->name);
@@ -941,7 +968,10 @@ static void abs_printout(int cpu, int nr, struct perf_evsel *evsel, double avg)
941 968
942 if (total && avg) { 969 if (total && avg) {
943 ratio = total / avg; 970 ratio = total / avg;
944 fprintf(output, "\n # %5.2f stalled cycles per insn", ratio); 971 fprintf(output, "\n");
972 if (aggr_mode == AGGR_NONE)
973 fprintf(output, " ");
974 fprintf(output, " # %5.2f stalled cycles per insn", ratio);
945 } 975 }
946 976
947 } else if (perf_evsel__match(evsel, HARDWARE, HW_BRANCH_MISSES) && 977 } else if (perf_evsel__match(evsel, HARDWARE, HW_BRANCH_MISSES) &&
@@ -1061,6 +1091,7 @@ static void print_aggr(char *prefix)
1061{ 1091{
1062 struct perf_evsel *counter; 1092 struct perf_evsel *counter;
1063 int cpu, cpu2, s, s2, id, nr; 1093 int cpu, cpu2, s, s2, id, nr;
1094 double uval;
1064 u64 ena, run, val; 1095 u64 ena, run, val;
1065 1096
1066 if (!(aggr_map || aggr_get_id)) 1097 if (!(aggr_map || aggr_get_id))
@@ -1087,11 +1118,17 @@ static void print_aggr(char *prefix)
1087 if (run == 0 || ena == 0) { 1118 if (run == 0 || ena == 0) {
1088 aggr_printout(counter, id, nr); 1119 aggr_printout(counter, id, nr);
1089 1120
1090 fprintf(output, "%*s%s%*s", 1121 fprintf(output, "%*s%s",
1091 csv_output ? 0 : 18, 1122 csv_output ? 0 : 18,
1092 counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED, 1123 counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED,
1093 csv_sep, 1124 csv_sep);
1094 csv_output ? 0 : -24, 1125
1126 fprintf(output, "%-*s%s",
1127 csv_output ? 0 : unit_width,
1128 counter->unit, csv_sep);
1129
1130 fprintf(output, "%*s",
1131 csv_output ? 0 : -25,
1095 perf_evsel__name(counter)); 1132 perf_evsel__name(counter));
1096 1133
1097 if (counter->cgrp) 1134 if (counter->cgrp)
@@ -1101,11 +1138,12 @@ static void print_aggr(char *prefix)
1101 fputc('\n', output); 1138 fputc('\n', output);
1102 continue; 1139 continue;
1103 } 1140 }
1141 uval = val * counter->scale;
1104 1142
1105 if (nsec_counter(counter)) 1143 if (nsec_counter(counter))
1106 nsec_printout(id, nr, counter, val); 1144 nsec_printout(id, nr, counter, uval);
1107 else 1145 else
1108 abs_printout(id, nr, counter, val); 1146 abs_printout(id, nr, counter, uval);
1109 1147
1110 if (!csv_output) { 1148 if (!csv_output) {
1111 print_noise(counter, 1.0); 1149 print_noise(counter, 1.0);
@@ -1128,16 +1166,21 @@ static void print_counter_aggr(struct perf_evsel *counter, char *prefix)
1128 struct perf_stat *ps = counter->priv; 1166 struct perf_stat *ps = counter->priv;
1129 double avg = avg_stats(&ps->res_stats[0]); 1167 double avg = avg_stats(&ps->res_stats[0]);
1130 int scaled = counter->counts->scaled; 1168 int scaled = counter->counts->scaled;
1169 double uval;
1131 1170
1132 if (prefix) 1171 if (prefix)
1133 fprintf(output, "%s", prefix); 1172 fprintf(output, "%s", prefix);
1134 1173
1135 if (scaled == -1) { 1174 if (scaled == -1) {
1136 fprintf(output, "%*s%s%*s", 1175 fprintf(output, "%*s%s",
1137 csv_output ? 0 : 18, 1176 csv_output ? 0 : 18,
1138 counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED, 1177 counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED,
1139 csv_sep, 1178 csv_sep);
1140 csv_output ? 0 : -24, 1179 fprintf(output, "%-*s%s",
1180 csv_output ? 0 : unit_width,
1181 counter->unit, csv_sep);
1182 fprintf(output, "%*s",
1183 csv_output ? 0 : -25,
1141 perf_evsel__name(counter)); 1184 perf_evsel__name(counter));
1142 1185
1143 if (counter->cgrp) 1186 if (counter->cgrp)
@@ -1147,10 +1190,12 @@ static void print_counter_aggr(struct perf_evsel *counter, char *prefix)
1147 return; 1190 return;
1148 } 1191 }
1149 1192
1193 uval = avg * counter->scale;
1194
1150 if (nsec_counter(counter)) 1195 if (nsec_counter(counter))
1151 nsec_printout(-1, 0, counter, avg); 1196 nsec_printout(-1, 0, counter, uval);
1152 else 1197 else
1153 abs_printout(-1, 0, counter, avg); 1198 abs_printout(-1, 0, counter, uval);
1154 1199
1155 print_noise(counter, avg); 1200 print_noise(counter, avg);
1156 1201
@@ -1177,6 +1222,7 @@ static void print_counter_aggr(struct perf_evsel *counter, char *prefix)
1177static void print_counter(struct perf_evsel *counter, char *prefix) 1222static void print_counter(struct perf_evsel *counter, char *prefix)
1178{ 1223{
1179 u64 ena, run, val; 1224 u64 ena, run, val;
1225 double uval;
1180 int cpu; 1226 int cpu;
1181 1227
1182 for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) { 1228 for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) {
@@ -1188,14 +1234,20 @@ static void print_counter(struct perf_evsel *counter, char *prefix)
1188 fprintf(output, "%s", prefix); 1234 fprintf(output, "%s", prefix);
1189 1235
1190 if (run == 0 || ena == 0) { 1236 if (run == 0 || ena == 0) {
1191 fprintf(output, "CPU%*d%s%*s%s%*s", 1237 fprintf(output, "CPU%*d%s%*s%s",
1192 csv_output ? 0 : -4, 1238 csv_output ? 0 : -4,
1193 perf_evsel__cpus(counter)->map[cpu], csv_sep, 1239 perf_evsel__cpus(counter)->map[cpu], csv_sep,
1194 csv_output ? 0 : 18, 1240 csv_output ? 0 : 18,
1195 counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED, 1241 counter->supported ? CNTR_NOT_COUNTED : CNTR_NOT_SUPPORTED,
1196 csv_sep, 1242 csv_sep);
1197 csv_output ? 0 : -24, 1243
1198 perf_evsel__name(counter)); 1244 fprintf(output, "%-*s%s",
1245 csv_output ? 0 : unit_width,
1246 counter->unit, csv_sep);
1247
1248 fprintf(output, "%*s",
1249 csv_output ? 0 : -25,
1250 perf_evsel__name(counter));
1199 1251
1200 if (counter->cgrp) 1252 if (counter->cgrp)
1201 fprintf(output, "%s%s", 1253 fprintf(output, "%s%s",
@@ -1205,10 +1257,12 @@ static void print_counter(struct perf_evsel *counter, char *prefix)
1205 continue; 1257 continue;
1206 } 1258 }
1207 1259
1260 uval = val * counter->scale;
1261
1208 if (nsec_counter(counter)) 1262 if (nsec_counter(counter))
1209 nsec_printout(cpu, 0, counter, val); 1263 nsec_printout(cpu, 0, counter, uval);
1210 else 1264 else
1211 abs_printout(cpu, 0, counter, val); 1265 abs_printout(cpu, 0, counter, uval);
1212 1266
1213 if (!csv_output) { 1267 if (!csv_output) {
1214 print_noise(counter, 1.0); 1268 print_noise(counter, 1.0);
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index 46dd4c2a41ce..dad64926170f 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -162,6 +162,8 @@ void perf_evsel__init(struct perf_evsel *evsel,
162 evsel->idx = idx; 162 evsel->idx = idx;
163 evsel->attr = *attr; 163 evsel->attr = *attr;
164 evsel->leader = evsel; 164 evsel->leader = evsel;
165 evsel->unit = "";
166 evsel->scale = 1.0;
165 INIT_LIST_HEAD(&evsel->node); 167 INIT_LIST_HEAD(&evsel->node);
166 hists__init(&evsel->hists); 168 hists__init(&evsel->hists);
167 evsel->sample_size = __perf_evsel__sample_size(attr->sample_type); 169 evsel->sample_size = __perf_evsel__sample_size(attr->sample_type);
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
index 1ea7c92e6e33..8120eeb86ac1 100644
--- a/tools/perf/util/evsel.h
+++ b/tools/perf/util/evsel.h
@@ -68,6 +68,8 @@ struct perf_evsel {
68 u32 ids; 68 u32 ids;
69 struct hists hists; 69 struct hists hists;
70 char *name; 70 char *name;
71 double scale;
72 const char *unit;
71 struct event_format *tp_format; 73 struct event_format *tp_format;
72 union { 74 union {
73 void *priv; 75 void *priv;
@@ -138,6 +140,7 @@ extern const char *perf_evsel__sw_names[PERF_COUNT_SW_MAX];
138int __perf_evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result, 140int __perf_evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result,
139 char *bf, size_t size); 141 char *bf, size_t size);
140const char *perf_evsel__name(struct perf_evsel *evsel); 142const char *perf_evsel__name(struct perf_evsel *evsel);
143
141const char *perf_evsel__group_name(struct perf_evsel *evsel); 144const char *perf_evsel__group_name(struct perf_evsel *evsel);
142int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size); 145int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size);
143 146
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 6de6f89c2a61..969cb8f0d88d 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -269,9 +269,10 @@ const char *event_type(int type)
269 269
270 270
271 271
272static int __add_event(struct list_head *list, int *idx, 272static struct perf_evsel *
273 struct perf_event_attr *attr, 273__add_event(struct list_head *list, int *idx,
274 char *name, struct cpu_map *cpus) 274 struct perf_event_attr *attr,
275 char *name, struct cpu_map *cpus)
275{ 276{
276 struct perf_evsel *evsel; 277 struct perf_evsel *evsel;
277 278
@@ -279,19 +280,19 @@ static int __add_event(struct list_head *list, int *idx,
279 280
280 evsel = perf_evsel__new_idx(attr, (*idx)++); 281 evsel = perf_evsel__new_idx(attr, (*idx)++);
281 if (!evsel) 282 if (!evsel)
282 return -ENOMEM; 283 return NULL;
283 284
284 evsel->cpus = cpus; 285 evsel->cpus = cpus;
285 if (name) 286 if (name)
286 evsel->name = strdup(name); 287 evsel->name = strdup(name);
287 list_add_tail(&evsel->node, list); 288 list_add_tail(&evsel->node, list);
288 return 0; 289 return evsel;
289} 290}
290 291
291static int add_event(struct list_head *list, int *idx, 292static int add_event(struct list_head *list, int *idx,
292 struct perf_event_attr *attr, char *name) 293 struct perf_event_attr *attr, char *name)
293{ 294{
294 return __add_event(list, idx, attr, name, NULL); 295 return __add_event(list, idx, attr, name, NULL) ? 0 : -ENOMEM;
295} 296}
296 297
297static int parse_aliases(char *str, const char *names[][PERF_EVSEL__MAX_ALIASES], int size) 298static int parse_aliases(char *str, const char *names[][PERF_EVSEL__MAX_ALIASES], int size)
@@ -633,6 +634,9 @@ int parse_events_add_pmu(struct list_head *list, int *idx,
633{ 634{
634 struct perf_event_attr attr; 635 struct perf_event_attr attr;
635 struct perf_pmu *pmu; 636 struct perf_pmu *pmu;
637 struct perf_evsel *evsel;
638 char *unit;
639 double scale;
636 640
637 pmu = perf_pmu__find(name); 641 pmu = perf_pmu__find(name);
638 if (!pmu) 642 if (!pmu)
@@ -640,7 +644,7 @@ int parse_events_add_pmu(struct list_head *list, int *idx,
640 644
641 memset(&attr, 0, sizeof(attr)); 645 memset(&attr, 0, sizeof(attr));
642 646
643 if (perf_pmu__check_alias(pmu, head_config)) 647 if (perf_pmu__check_alias(pmu, head_config, &unit, &scale))
644 return -EINVAL; 648 return -EINVAL;
645 649
646 /* 650 /*
@@ -652,8 +656,14 @@ int parse_events_add_pmu(struct list_head *list, int *idx,
652 if (perf_pmu__config(pmu, &attr, head_config)) 656 if (perf_pmu__config(pmu, &attr, head_config))
653 return -EINVAL; 657 return -EINVAL;
654 658
655 return __add_event(list, idx, &attr, pmu_event_name(head_config), 659 evsel = __add_event(list, idx, &attr, pmu_event_name(head_config),
656 pmu->cpus); 660 pmu->cpus);
661 if (evsel) {
662 evsel->unit = unit;
663 evsel->scale = scale;
664 }
665
666 return evsel ? 0 : -ENOMEM;
657} 667}
658 668
659int parse_events__modifier_group(struct list_head *list, 669int parse_events__modifier_group(struct list_head *list,
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index c232d8dd410b..56fc10a5e288 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -1,19 +1,23 @@
1#include <linux/list.h> 1#include <linux/list.h>
2#include <sys/types.h> 2#include <sys/types.h>
3#include <sys/stat.h>
4#include <unistd.h> 3#include <unistd.h>
5#include <stdio.h> 4#include <stdio.h>
6#include <dirent.h> 5#include <dirent.h>
7#include "fs.h" 6#include "fs.h"
7#include <locale.h>
8#include "util.h" 8#include "util.h"
9#include "pmu.h" 9#include "pmu.h"
10#include "parse-events.h" 10#include "parse-events.h"
11#include "cpumap.h" 11#include "cpumap.h"
12 12
13#define UNIT_MAX_LEN 31 /* max length for event unit name */
14
13struct perf_pmu_alias { 15struct perf_pmu_alias {
14 char *name; 16 char *name;
15 struct list_head terms; 17 struct list_head terms;
16 struct list_head list; 18 struct list_head list;
19 char unit[UNIT_MAX_LEN+1];
20 double scale;
17}; 21};
18 22
19struct perf_pmu_format { 23struct perf_pmu_format {
@@ -94,7 +98,80 @@ static int pmu_format(const char *name, struct list_head *format)
94 return 0; 98 return 0;
95} 99}
96 100
97static int perf_pmu__new_alias(struct list_head *list, char *name, FILE *file) 101static int perf_pmu__parse_scale(struct perf_pmu_alias *alias, char *dir, char *name)
102{
103 struct stat st;
104 ssize_t sret;
105 char scale[128];
106 int fd, ret = -1;
107 char path[PATH_MAX];
108 char *lc;
109
110 snprintf(path, PATH_MAX, "%s/%s.scale", dir, name);
111
112 fd = open(path, O_RDONLY);
113 if (fd == -1)
114 return -1;
115
116 if (fstat(fd, &st) < 0)
117 goto error;
118
119 sret = read(fd, scale, sizeof(scale)-1);
120 if (sret < 0)
121 goto error;
122
123 scale[sret] = '\0';
124 /*
125 * save current locale
126 */
127 lc = setlocale(LC_NUMERIC, NULL);
128
129 /*
130 * force to C locale to ensure kernel
131 * scale string is converted correctly.
132 * kernel uses default C locale.
133 */
134 setlocale(LC_NUMERIC, "C");
135
136 alias->scale = strtod(scale, NULL);
137
138 /* restore locale */
139 setlocale(LC_NUMERIC, lc);
140
141 ret = 0;
142error:
143 close(fd);
144 return ret;
145}
146
147static int perf_pmu__parse_unit(struct perf_pmu_alias *alias, char *dir, char *name)
148{
149 char path[PATH_MAX];
150 ssize_t sret;
151 int fd;
152
153 snprintf(path, PATH_MAX, "%s/%s.unit", dir, name);
154
155 fd = open(path, O_RDONLY);
156 if (fd == -1)
157 return -1;
158
159 sret = read(fd, alias->unit, UNIT_MAX_LEN);
160 if (sret < 0)
161 goto error;
162
163 close(fd);
164
165 alias->unit[sret] = '\0';
166
167 return 0;
168error:
169 close(fd);
170 alias->unit[0] = '\0';
171 return -1;
172}
173
174static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FILE *file)
98{ 175{
99 struct perf_pmu_alias *alias; 176 struct perf_pmu_alias *alias;
100 char buf[256]; 177 char buf[256];
@@ -110,6 +187,9 @@ static int perf_pmu__new_alias(struct list_head *list, char *name, FILE *file)
110 return -ENOMEM; 187 return -ENOMEM;
111 188
112 INIT_LIST_HEAD(&alias->terms); 189 INIT_LIST_HEAD(&alias->terms);
190 alias->scale = 1.0;
191 alias->unit[0] = '\0';
192
113 ret = parse_events_terms(&alias->terms, buf); 193 ret = parse_events_terms(&alias->terms, buf);
114 if (ret) { 194 if (ret) {
115 free(alias); 195 free(alias);
@@ -117,7 +197,14 @@ static int perf_pmu__new_alias(struct list_head *list, char *name, FILE *file)
117 } 197 }
118 198
119 alias->name = strdup(name); 199 alias->name = strdup(name);
200 /*
201 * load unit name and scale if available
202 */
203 perf_pmu__parse_unit(alias, dir, name);
204 perf_pmu__parse_scale(alias, dir, name);
205
120 list_add_tail(&alias->list, list); 206 list_add_tail(&alias->list, list);
207
121 return 0; 208 return 0;
122} 209}
123 210
@@ -129,6 +216,7 @@ static int pmu_aliases_parse(char *dir, struct list_head *head)
129{ 216{
130 struct dirent *evt_ent; 217 struct dirent *evt_ent;
131 DIR *event_dir; 218 DIR *event_dir;
219 size_t len;
132 int ret = 0; 220 int ret = 0;
133 221
134 event_dir = opendir(dir); 222 event_dir = opendir(dir);
@@ -143,13 +231,24 @@ static int pmu_aliases_parse(char *dir, struct list_head *head)
143 if (!strcmp(name, ".") || !strcmp(name, "..")) 231 if (!strcmp(name, ".") || !strcmp(name, ".."))
144 continue; 232 continue;
145 233
234 /*
235 * skip .unit and .scale info files
236 * parsed in perf_pmu__new_alias()
237 */
238 len = strlen(name);
239 if (len > 5 && !strcmp(name + len - 5, ".unit"))
240 continue;
241 if (len > 6 && !strcmp(name + len - 6, ".scale"))
242 continue;
243
146 snprintf(path, PATH_MAX, "%s/%s", dir, name); 244 snprintf(path, PATH_MAX, "%s/%s", dir, name);
147 245
148 ret = -EINVAL; 246 ret = -EINVAL;
149 file = fopen(path, "r"); 247 file = fopen(path, "r");
150 if (!file) 248 if (!file)
151 break; 249 break;
152 ret = perf_pmu__new_alias(head, name, file); 250
251 ret = perf_pmu__new_alias(head, dir, name, file);
153 fclose(file); 252 fclose(file);
154 } 253 }
155 254
@@ -508,16 +607,42 @@ static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu,
508 return NULL; 607 return NULL;
509} 608}
510 609
610
611static int check_unit_scale(struct perf_pmu_alias *alias,
612 char **unit, double *scale)
613{
614 /*
615 * Only one term in event definition can
616 * define unit and scale, fail if there's
617 * more than one.
618 */
619 if ((*unit && alias->unit) ||
620 (*scale && alias->scale))
621 return -EINVAL;
622
623 if (alias->unit)
624 *unit = alias->unit;
625
626 if (alias->scale)
627 *scale = alias->scale;
628
629 return 0;
630}
631
511/* 632/*
512 * Find alias in the terms list and replace it with the terms 633 * Find alias in the terms list and replace it with the terms
513 * defined for the alias 634 * defined for the alias
514 */ 635 */
515int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms) 636int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms,
637 char **unit, double *scale)
516{ 638{
517 struct parse_events_term *term, *h; 639 struct parse_events_term *term, *h;
518 struct perf_pmu_alias *alias; 640 struct perf_pmu_alias *alias;
519 int ret; 641 int ret;
520 642
643 *unit = NULL;
644 *scale = 0;
645
521 list_for_each_entry_safe(term, h, head_terms, list) { 646 list_for_each_entry_safe(term, h, head_terms, list) {
522 alias = pmu_find_alias(pmu, term); 647 alias = pmu_find_alias(pmu, term);
523 if (!alias) 648 if (!alias)
@@ -525,6 +650,11 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms)
525 ret = pmu_alias_terms(alias, &term->list); 650 ret = pmu_alias_terms(alias, &term->list);
526 if (ret) 651 if (ret)
527 return ret; 652 return ret;
653
654 ret = check_unit_scale(alias, unit, scale);
655 if (ret)
656 return ret;
657
528 list_del(&term->list); 658 list_del(&term->list);
529 free(term); 659 free(term);
530 } 660 }
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index 1179b26f244a..9183380e2038 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -28,7 +28,8 @@ int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
28int perf_pmu__config_terms(struct list_head *formats, 28int perf_pmu__config_terms(struct list_head *formats,
29 struct perf_event_attr *attr, 29 struct perf_event_attr *attr,
30 struct list_head *head_terms); 30 struct list_head *head_terms);
31int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms); 31int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms,
32 char **unit, double *scale);
32struct list_head *perf_pmu__alias(struct perf_pmu *pmu, 33struct list_head *perf_pmu__alias(struct perf_pmu *pmu,
33 struct list_head *head_terms); 34 struct list_head *head_terms);
34int perf_pmu_wrap(void); 35int perf_pmu_wrap(void);