Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf tools: Improve handling of hybrid PMUs in perf_event_attr__fprintf

Support the PMU name from the legacy hardware and hw_cache PMU
extended types. Remove some macros and make variables more intention
revealing, rather than just being called "value".

Before:
```
$ perf stat -vv -e instructions true
...
------------------------------------------------------------
perf_event_attr:
type 0 (PERF_TYPE_HARDWARE)
size 136
config 0xa00000001
sample_type IDENTIFIER
read_format TOTAL_TIME_ENABLED|TOTAL_TIME_RUNNING
disabled 1
inherit 1
enable_on_exec 1
exclude_guest 1
------------------------------------------------------------
sys_perf_event_open: pid 181636 cpu -1 group_fd -1 flags 0x8 = 5
------------------------------------------------------------
perf_event_attr:
type 0 (PERF_TYPE_HARDWARE)
size 136
config 0x400000001
sample_type IDENTIFIER
read_format TOTAL_TIME_ENABLED|TOTAL_TIME_RUNNING
disabled 1
inherit 1
enable_on_exec 1
exclude_guest 1
------------------------------------------------------------
sys_perf_event_open: pid 181636 cpu -1 group_fd -1 flags 0x8 = 6
...
```

After:
```
$ perf stat -vv -e instructions true
...
------------------------------------------------------------
perf_event_attr:
type 0 (PERF_TYPE_HARDWARE)
size 136
config 0xa00000001 (cpu_atom/PERF_COUNT_HW_INSTRUCTIONS/)
sample_type IDENTIFIER
read_format TOTAL_TIME_ENABLED|TOTAL_TIME_RUNNING
disabled 1
inherit 1
enable_on_exec 1
------------------------------------------------------------
sys_perf_event_open: pid 181724 cpu -1 group_fd -1 flags 0x8 = 5
------------------------------------------------------------
perf_event_attr:
type 0 (PERF_TYPE_HARDWARE)
size 136
config 0x400000001 (cpu_core/PERF_COUNT_HW_INSTRUCTIONS/)
sample_type IDENTIFIER
read_format TOTAL_TIME_ENABLED|TOTAL_TIME_RUNNING
disabled 1
inherit 1
enable_on_exec 1
------------------------------------------------------------
sys_perf_event_open: pid 181724 cpu -1 group_fd -1 flags 0x8 = 6
...
```

Signed-off-by: Ian Rogers <irogers@google.com>
Reviewed-by: James Clark <james.clark@linaro.org>
Tested-by: Thomas Falcon <thomas.falcon@intel.com>
Tested-by: James Clark <james.clark@linaro.org>
Link: https://lore.kernel.org/r/20250307023906.1135613-1-irogers@google.com
Signed-off-by: Namhyung Kim <namhyung@kernel.org>

authored by

Ian Rogers and committed by
Namhyung Kim
fd5de637 f7cffbab

+83 -57
+83 -57
tools/perf/util/perf_event_attr_fprintf.c
··· 79 79 #define ENUM_ID_TO_STR_CASE(x) case x: return (#x); 80 80 static const char *stringify_perf_type_id(struct perf_pmu *pmu, u32 type) 81 81 { 82 - if (pmu) 83 - return pmu->name; 84 - 85 82 switch (type) { 86 83 ENUM_ID_TO_STR_CASE(PERF_TYPE_HARDWARE) 87 84 ENUM_ID_TO_STR_CASE(PERF_TYPE_SOFTWARE) 88 85 ENUM_ID_TO_STR_CASE(PERF_TYPE_TRACEPOINT) 89 86 ENUM_ID_TO_STR_CASE(PERF_TYPE_HW_CACHE) 90 - ENUM_ID_TO_STR_CASE(PERF_TYPE_RAW) 91 87 ENUM_ID_TO_STR_CASE(PERF_TYPE_BREAKPOINT) 88 + case PERF_TYPE_RAW: 89 + return pmu ? pmu->name : "PERF_TYPE_RAW"; 92 90 default: 93 - return NULL; 91 + return pmu ? pmu->name : NULL; 94 92 } 95 93 } 96 94 97 95 static const char *stringify_perf_hw_id(u64 value) 98 96 { 99 - switch (value) { 97 + switch (value & PERF_HW_EVENT_MASK) { 100 98 ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_CPU_CYCLES) 101 99 ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_INSTRUCTIONS) 102 100 ENUM_ID_TO_STR_CASE(PERF_COUNT_HW_CACHE_REFERENCES) ··· 167 169 } 168 170 #undef ENUM_ID_TO_STR_CASE 169 171 170 - #define PRINT_ID(_s, _f) \ 171 - do { \ 172 - const char *__s = _s; \ 173 - if (__s == NULL) \ 174 - snprintf(buf, size, _f, value); \ 175 - else \ 176 - snprintf(buf, size, _f" (%s)", value, __s); \ 177 - } while (0) 178 - #define print_id_unsigned(_s) PRINT_ID(_s, "%"PRIu64) 179 - #define print_id_hex(_s) PRINT_ID(_s, "%#"PRIx64) 180 - 181 - static void __p_type_id(struct perf_pmu *pmu, char *buf, size_t size, u64 value) 172 + static void print_id_unsigned(char *buf, size_t size, u64 value, const char *s) 182 173 { 183 - print_id_unsigned(stringify_perf_type_id(pmu, value)); 174 + if (s == NULL) 175 + snprintf(buf, size, "%"PRIu64, value); 176 + else 177 + snprintf(buf, size, "%"PRIu64" (%s)", value, s); 184 178 } 185 179 186 - static void __p_config_hw_id(char *buf, size_t size, u64 value) 180 + static void print_id_hex(char *buf, size_t size, u64 value, const char *s) 187 181 { 188 - print_id_hex(stringify_perf_hw_id(value)); 189 - } 190 - 191 - static void __p_config_sw_id(char *buf, size_t size, u64 value) 192 - { 193 - print_id_hex(stringify_perf_sw_id(value)); 194 - } 195 - 196 - static void __p_config_hw_cache_id(char *buf, size_t size, u64 value) 197 - { 198 - const char *hw_cache_str = stringify_perf_hw_cache_id(value & 0xff); 199 - const char *hw_cache_op_str = 200 - stringify_perf_hw_cache_op_id((value & 0xff00) >> 8); 201 - const char *hw_cache_op_result_str = 202 - stringify_perf_hw_cache_op_result_id((value & 0xff0000) >> 16); 203 - 204 - if (hw_cache_str == NULL || hw_cache_op_str == NULL || 205 - hw_cache_op_result_str == NULL) { 182 + if (s == NULL) 206 183 snprintf(buf, size, "%#"PRIx64, value); 184 + else 185 + snprintf(buf, size, "%#"PRIx64" (%s)", value, s); 186 + } 187 + 188 + static void __p_type_id(char *buf, size_t size, struct perf_pmu *pmu, u32 type) 189 + { 190 + print_id_unsigned(buf, size, type, stringify_perf_type_id(pmu, type)); 191 + } 192 + 193 + static void __p_config_hw_id(char *buf, size_t size, struct perf_pmu *pmu, u64 config) 194 + { 195 + const char *name = stringify_perf_hw_id(config); 196 + 197 + if (name == NULL) { 198 + if (pmu == NULL) { 199 + snprintf(buf, size, "%#"PRIx64, config); 200 + } else { 201 + snprintf(buf, size, "%#"PRIx64" (%s/config=%#"PRIx64"/)", config, pmu->name, 202 + config); 203 + } 207 204 } else { 208 - snprintf(buf, size, "%#"PRIx64" (%s | %s | %s)", value, 209 - hw_cache_op_result_str, hw_cache_op_str, hw_cache_str); 205 + if (pmu == NULL) 206 + snprintf(buf, size, "%#"PRIx64" (%s)", config, name); 207 + else 208 + snprintf(buf, size, "%#"PRIx64" (%s/%s/)", config, pmu->name, name); 210 209 } 211 210 } 212 211 213 - static void __p_config_tracepoint_id(char *buf, size_t size, u64 value) 212 + static void __p_config_sw_id(char *buf, size_t size, u64 id) 214 213 { 215 - char *str = tracepoint_id_to_name(value); 214 + print_id_hex(buf, size, id, stringify_perf_sw_id(id)); 215 + } 216 216 217 - print_id_hex(str); 217 + static void __p_config_hw_cache_id(char *buf, size_t size, struct perf_pmu *pmu, u64 config) 218 + { 219 + const char *hw_cache_str = stringify_perf_hw_cache_id(config & 0xff); 220 + const char *hw_cache_op_str = 221 + stringify_perf_hw_cache_op_id((config & 0xff00) >> 8); 222 + const char *hw_cache_op_result_str = 223 + stringify_perf_hw_cache_op_result_id((config & 0xff0000) >> 16); 224 + 225 + if (hw_cache_str == NULL || hw_cache_op_str == NULL || hw_cache_op_result_str == NULL) { 226 + if (pmu == NULL) { 227 + snprintf(buf, size, "%#"PRIx64, config); 228 + } else { 229 + snprintf(buf, size, "%#"PRIx64" (%s/config=%#"PRIx64"/)", config, pmu->name, 230 + config); 231 + } 232 + } else { 233 + if (pmu == NULL) { 234 + snprintf(buf, size, "%#"PRIx64" (%s | %s | %s)", config, 235 + hw_cache_op_result_str, hw_cache_op_str, hw_cache_str); 236 + } else { 237 + snprintf(buf, size, "%#"PRIx64" (%s/%s | %s | %s/)", config, pmu->name, 238 + hw_cache_op_result_str, hw_cache_op_str, hw_cache_str); 239 + } 240 + } 241 + } 242 + 243 + static void __p_config_tracepoint_id(char *buf, size_t size, u64 id) 244 + { 245 + char *str = tracepoint_id_to_name(id); 246 + 247 + print_id_hex(buf, size, id, str); 218 248 free(str); 219 249 } 220 250 221 - static void __p_config_id(struct perf_pmu *pmu, char *buf, size_t size, u32 type, u64 value) 251 + static void __p_config_id(struct perf_pmu *pmu, char *buf, size_t size, u32 type, u64 config) 222 252 { 223 - const char *name = perf_pmu__name_from_config(pmu, value); 224 - 225 - if (name) { 226 - print_id_hex(name); 227 - return; 228 - } 229 253 switch (type) { 230 254 case PERF_TYPE_HARDWARE: 231 - return __p_config_hw_id(buf, size, value); 255 + return __p_config_hw_id(buf, size, pmu, config); 232 256 case PERF_TYPE_SOFTWARE: 233 - return __p_config_sw_id(buf, size, value); 257 + return __p_config_sw_id(buf, size, config); 234 258 case PERF_TYPE_HW_CACHE: 235 - return __p_config_hw_cache_id(buf, size, value); 259 + return __p_config_hw_cache_id(buf, size, pmu, config); 236 260 case PERF_TYPE_TRACEPOINT: 237 - return __p_config_tracepoint_id(buf, size, value); 261 + return __p_config_tracepoint_id(buf, size, config); 238 262 case PERF_TYPE_RAW: 239 263 case PERF_TYPE_BREAKPOINT: 240 264 default: 241 - snprintf(buf, size, "%#"PRIx64, value); 242 - return; 265 + return print_id_hex(buf, size, config, perf_pmu__name_from_config(pmu, config)); 243 266 } 244 267 } 245 268 ··· 272 253 #define p_sample_type(val) __p_sample_type(buf, BUF_SIZE, val) 273 254 #define p_branch_sample_type(val) __p_branch_sample_type(buf, BUF_SIZE, val) 274 255 #define p_read_format(val) __p_read_format(buf, BUF_SIZE, val) 275 - #define p_type_id(val) __p_type_id(pmu, buf, BUF_SIZE, val) 256 + #define p_type_id(val) __p_type_id(buf, BUF_SIZE, pmu, val) 276 257 #define p_config_id(val) __p_config_id(pmu, buf, BUF_SIZE, attr->type, val) 277 258 278 259 #define PRINT_ATTRn(_n, _f, _p, _a) \ ··· 291 272 struct perf_pmu *pmu = perf_pmus__find_by_type(attr->type); 292 273 char buf[BUF_SIZE]; 293 274 int ret = 0; 275 + 276 + if (!pmu && (attr->type == PERF_TYPE_HARDWARE || attr->type == PERF_TYPE_HW_CACHE)) { 277 + u32 extended_type = attr->config >> PERF_PMU_TYPE_SHIFT; 278 + 279 + if (extended_type) 280 + pmu = perf_pmus__find_by_type(extended_type); 281 + } 294 282 295 283 PRINT_ATTRn("type", type, p_type_id, true); 296 284 PRINT_ATTRf(size, p_unsigned);