diff options
author | Thomas Richter <tmricht@linux.ibm.com> | 2018-06-15 06:11:05 -0400 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2018-06-25 10:59:37 -0400 |
commit | 6dde6429c5ff5b38d6d40a14a6ee105117e6364d (patch) | |
tree | 1a15fc00cee2ecafdc4a74297ce069e1af0c04c1 /tools/perf | |
parent | 0c24d6fb7bd3578e5b9e4972d01bbe3d087ded33 (diff) |
perf stat: Remove duplicate event counting
'perf stat' shows a mismatch in perf stat regarding counter names on
s390:
Run command:
[root@s35lp76 perf]# ./perf stat -e tx_nc_tend -v --
~/mytesttx 1 >/tmp/111
tx_nc_tend: 1 573146 573146
tx_nc_tend: 1 573146 573146
Performance counter stats for '/root/mytesttx 1':
3 tx_nc_tend
0.001037252 seconds time elapsed
[root@s35lp76 perf]#
shows transaction counter tx_nc_tend with value 3 but it was triggered
only once as seen by the output of mytesttx.
When looking up the event name tx_nc_tend the following function
sequence is called:
parse_events_multi_pmu_add()
+--> perf_pmu__scan() being called with NULL argument
+--> pmu_read_sysfs() scans directory ../devices/ for
all PMUs
+--> perf_pmu__find() tries to find a PMU in the
global pmu list.
+--> pmu_lookup() called to read all file
entries when not in global
list.
pmu_lookup() causes the issue. It calls
+---> pmu_aliases() to read all the entries in the PMU directory.
On s390 this is named
/sys/devices/cpum_cf/events.
+--> pmu_aliases_parse() reads all files and creates an
alias for each file name.
So we end up with first entry created by
reading the sysfs file
[root@s35lp76 perf]# cat /sys/devices/cpum_cf
/events/TX_NC_TEND
event=0x008d
[root@s35lp76 perf]#
Debug output shows this entry
tx_nc_tend -> 'cpum_cf'/'event=0x008d
'/
After all files in this directory have been
read and aliases created this function is called:
+--> pmu_add_cpu_aliases()
This function looks up the CPU tables
created by the json files.
With json files for s390 now available all
the aliases are added to
the PMU alias list a second time.
The second entry is added by
reading the json file converted by jevent
resulting in file pmu-events/pmu-events.c:
{
.name = "tx_nc_tend",
.event = "event=0x8d",
.desc = "Unit: cpum_cf Completed TEND \
instructions \
in non-constrained TX mode",
.topic = "extended",
.long_desc = "A TEND instruction has \
completed in a \
non-constrained \
transactional-execution mode",
.pmu = "cpum_cf",
},
Debug output shows this entry
tx_nc_tend -> 'cpum_cf'/'event=0x8d'/
Function pmu_aliases_parse() and pmu_add_cpu_aliases() both use
__perf_pmu__new_alias() to add an alias to the PMU alias list. There is
no check if an alias already exist
So we end up with 2 entries for tx_nc_tend in the PMU alias list.
Having set up the PMU alias list for this PMU now
parse_events_multi_add_pmu() reads the complete alias list and adds each
alias with parse_events_add_pmu() to the global perfev_list. This
causes the alias to be added multiple times to the event list.
Fix this by making __perf_pmu__new_alias() to merge alias definitions if
an alias is already on the alias list. Also print a debug message when
the alias has mismatches in some fields.
Output before:
[root@s35lp76 perf]# ./perf stat -e tx_nc_tend -v \
-- ~/mytesttx 1 >/tmp/111
tx_nc_tend: 1 551446 551446
Performance counter stats for '/root/mytesttx 1':
3 tx_nc_tend
0.000961134 seconds time elapsed
[root@s35lp76 perf]#
Output after:
[root@s35lp76 perf]# ./perf stat -e tx_nc_tend -v \
-- ~/mytesttx 1 >/tmp/111
tx_nc_tend: 1 551446 551446
Performance counter stats for '/root/mytesttx 1':
1 tx_nc_tend
0.000961134 seconds time elapsed
[root@s35lp76 perf]#
Signed-off-by: Thomas Richter <tmricht@linux.ibm.com>
Reviewed-by: Hendrik Brueckner <brueckner@linux.ibm.com>
Reviewed-by: Jiri Olsa <jolsa@redhat.com>
Cc: Heiko Carstens <heiko.carstens@de.ibm.com>
Cc: Martin Schwidefsky <schwidefsky@de.ibm.com>
Link: http://lkml.kernel.org/r/20180615101105.47047-3-tmricht@linux.ibm.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools/perf')
-rw-r--r-- | tools/perf/util/pmu.c | 71 |
1 files changed, 70 insertions, 1 deletions
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index f321ce97d9ec..3ba6a1742f91 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c | |||
@@ -234,6 +234,74 @@ static int perf_pmu__parse_snapshot(struct perf_pmu_alias *alias, | |||
234 | return 0; | 234 | return 0; |
235 | } | 235 | } |
236 | 236 | ||
237 | static void perf_pmu_assign_str(char *name, const char *field, char **old_str, | ||
238 | char **new_str) | ||
239 | { | ||
240 | if (!*old_str) | ||
241 | goto set_new; | ||
242 | |||
243 | if (*new_str) { /* Have new string, check with old */ | ||
244 | if (strcasecmp(*old_str, *new_str)) | ||
245 | pr_debug("alias %s differs in field '%s'\n", | ||
246 | name, field); | ||
247 | zfree(old_str); | ||
248 | } else /* Nothing new --> keep old string */ | ||
249 | return; | ||
250 | set_new: | ||
251 | *old_str = *new_str; | ||
252 | *new_str = NULL; | ||
253 | } | ||
254 | |||
255 | static void perf_pmu_update_alias(struct perf_pmu_alias *old, | ||
256 | struct perf_pmu_alias *newalias) | ||
257 | { | ||
258 | perf_pmu_assign_str(old->name, "desc", &old->desc, &newalias->desc); | ||
259 | perf_pmu_assign_str(old->name, "long_desc", &old->long_desc, | ||
260 | &newalias->long_desc); | ||
261 | perf_pmu_assign_str(old->name, "topic", &old->topic, &newalias->topic); | ||
262 | perf_pmu_assign_str(old->name, "metric_expr", &old->metric_expr, | ||
263 | &newalias->metric_expr); | ||
264 | perf_pmu_assign_str(old->name, "metric_name", &old->metric_name, | ||
265 | &newalias->metric_name); | ||
266 | perf_pmu_assign_str(old->name, "value", &old->str, &newalias->str); | ||
267 | old->scale = newalias->scale; | ||
268 | old->per_pkg = newalias->per_pkg; | ||
269 | old->snapshot = newalias->snapshot; | ||
270 | memcpy(old->unit, newalias->unit, sizeof(old->unit)); | ||
271 | } | ||
272 | |||
273 | /* Delete an alias entry. */ | ||
274 | static void perf_pmu_free_alias(struct perf_pmu_alias *newalias) | ||
275 | { | ||
276 | zfree(&newalias->name); | ||
277 | zfree(&newalias->desc); | ||
278 | zfree(&newalias->long_desc); | ||
279 | zfree(&newalias->topic); | ||
280 | zfree(&newalias->str); | ||
281 | zfree(&newalias->metric_expr); | ||
282 | zfree(&newalias->metric_name); | ||
283 | parse_events_terms__purge(&newalias->terms); | ||
284 | free(newalias); | ||
285 | } | ||
286 | |||
287 | /* Merge an alias, search in alias list. If this name is already | ||
288 | * present merge both of them to combine all information. | ||
289 | */ | ||
290 | static bool perf_pmu_merge_alias(struct perf_pmu_alias *newalias, | ||
291 | struct list_head *alist) | ||
292 | { | ||
293 | struct perf_pmu_alias *a; | ||
294 | |||
295 | list_for_each_entry(a, alist, list) { | ||
296 | if (!strcasecmp(newalias->name, a->name)) { | ||
297 | perf_pmu_update_alias(a, newalias); | ||
298 | perf_pmu_free_alias(newalias); | ||
299 | return true; | ||
300 | } | ||
301 | } | ||
302 | return false; | ||
303 | } | ||
304 | |||
237 | static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name, | 305 | static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name, |
238 | char *desc, char *val, | 306 | char *desc, char *val, |
239 | char *long_desc, char *topic, | 307 | char *long_desc, char *topic, |
@@ -310,7 +378,8 @@ static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name, | |||
310 | alias->per_pkg = perpkg && sscanf(perpkg, "%d", &num) == 1 && num == 1; | 378 | alias->per_pkg = perpkg && sscanf(perpkg, "%d", &num) == 1 && num == 1; |
311 | alias->str = strdup(newval); | 379 | alias->str = strdup(newval); |
312 | 380 | ||
313 | list_add_tail(&alias->list, list); | 381 | if (!perf_pmu_merge_alias(alias, list)) |
382 | list_add_tail(&alias->list, list); | ||
314 | 383 | ||
315 | return 0; | 384 | return 0; |
316 | } | 385 | } |