|
1 | 1 | // SPDX-License-Identifier: GPL-2.0
|
2 | 2 | #include <linux/list.h>
|
| 3 | +#include <linux/list_sort.h> |
| 4 | +#include <linux/string.h> |
3 | 5 | #include <linux/zalloc.h>
|
4 | 6 | #include <subcmd/pager.h>
|
5 | 7 | #include <sys/types.h>
|
| 8 | +#include <ctype.h> |
6 | 9 | #include <dirent.h>
|
7 | 10 | #include <pthread.h>
|
8 | 11 | #include <string.h>
|
@@ -33,6 +36,31 @@ static LIST_HEAD(other_pmus);
|
33 | 36 | static bool read_sysfs_core_pmus;
|
34 | 37 | static bool read_sysfs_all_pmus;
|
35 | 38 |
|
| 39 | +static int pmu_name_len_no_suffix(const char *str, unsigned long *num) |
| 40 | +{ |
| 41 | + int orig_len, len; |
| 42 | + |
| 43 | + orig_len = len = strlen(str); |
| 44 | + |
| 45 | + /* Non-uncore PMUs have their full length, for example, i915. */ |
| 46 | + if (!strstarts(str, "uncore_")) |
| 47 | + return len; |
| 48 | + |
| 49 | + /* |
| 50 | + * Count trailing digits and '_', if '_{num}' suffix isn't present use |
| 51 | + * the full length. |
| 52 | + */ |
| 53 | + while (len > 0 && isdigit(str[len - 1])) |
| 54 | + len--; |
| 55 | + |
| 56 | + if (len > 0 && len != orig_len && str[len - 1] == '_') { |
| 57 | + if (num) |
| 58 | + *num = strtoul(&str[len], NULL, 10); |
| 59 | + return len - 1; |
| 60 | + } |
| 61 | + return orig_len; |
| 62 | +} |
| 63 | + |
36 | 64 | void perf_pmus__destroy(void)
|
37 | 65 | {
|
38 | 66 | struct perf_pmu *pmu, *tmp;
|
@@ -122,6 +150,25 @@ static struct perf_pmu *perf_pmu__find2(int dirfd, const char *name)
|
122 | 150 | return perf_pmu__lookup(core_pmu ? &core_pmus : &other_pmus, dirfd, name);
|
123 | 151 | }
|
124 | 152 |
|
| 153 | +static int pmus_cmp(void *priv __maybe_unused, |
| 154 | + const struct list_head *lhs, const struct list_head *rhs) |
| 155 | +{ |
| 156 | + unsigned long lhs_num = 0, rhs_num = 0; |
| 157 | + struct perf_pmu *lhs_pmu = container_of(lhs, struct perf_pmu, list); |
| 158 | + struct perf_pmu *rhs_pmu = container_of(rhs, struct perf_pmu, list); |
| 159 | + const char *lhs_pmu_name = lhs_pmu->name ?: ""; |
| 160 | + const char *rhs_pmu_name = rhs_pmu->name ?: ""; |
| 161 | + int lhs_pmu_name_len = pmu_name_len_no_suffix(lhs_pmu_name, &lhs_num); |
| 162 | + int rhs_pmu_name_len = pmu_name_len_no_suffix(rhs_pmu_name, &rhs_num); |
| 163 | + int ret = strncmp(lhs_pmu_name, rhs_pmu_name, |
| 164 | + lhs_pmu_name_len < rhs_pmu_name_len ? lhs_pmu_name_len : rhs_pmu_name_len); |
| 165 | + |
| 166 | + if (lhs_pmu_name_len != rhs_pmu_name_len || ret != 0 || lhs_pmu_name_len == 0) |
| 167 | + return ret; |
| 168 | + |
| 169 | + return lhs_num < rhs_num ? -1 : (lhs_num > rhs_num ? 1 : 0); |
| 170 | +} |
| 171 | + |
125 | 172 | /* Add all pmus in sysfs to pmu list: */
|
126 | 173 | static void pmu_read_sysfs(bool core_only)
|
127 | 174 | {
|
@@ -156,6 +203,8 @@ static void pmu_read_sysfs(bool core_only)
|
156 | 203 | if (!perf_pmu__create_placeholder_core_pmu(&core_pmus))
|
157 | 204 | pr_err("Failure to set up any core PMUs\n");
|
158 | 205 | }
|
| 206 | + list_sort(NULL, &core_pmus, pmus_cmp); |
| 207 | + list_sort(NULL, &other_pmus, pmus_cmp); |
159 | 208 | if (!list_empty(&core_pmus)) {
|
160 | 209 | read_sysfs_core_pmus = true;
|
161 | 210 | if (!core_only)
|
|
0 commit comments