* contain hyphens and the longest name
* should always be selected.
*/
-int parse_events__decode_legacy_cache(const char *name, int pmu_type, __u64 *config)
+int parse_events__decode_legacy_cache(const char *name, int extended_pmu_type, __u64 *config)
{
int len, cache_type = -1, cache_op = -1, cache_result = -1;
const char *name_end = &name[strlen(name) + 1];
if (cache_result == -1)
cache_result = PERF_COUNT_HW_CACHE_RESULT_ACCESS;
- *config = ((__u64)pmu_type << PERF_PMU_TYPE_SHIFT) |
- cache_type | (cache_op << 8) | (cache_result << 16);
+ *config = cache_type | (cache_op << 8) | (cache_result << 16);
+ if (perf_pmus__supports_extended_type())
+ *config |= (__u64)extended_pmu_type << PERF_PMU_TYPE_SHIFT;
return 0;
}
const struct perf_pmu *pmu = perf_pmus__find_by_type(attr->type);
if (!pmu) {
- pr_debug("Failed to find PMU for type %d", attr->type);
+ char *err_str;
+
+ if (asprintf(&err_str, "Failed to find PMU for type %d", attr->type) >= 0)
+ parse_events_error__handle(err, term->err_term,
+ err_str, /*help=*/NULL);
return -EINVAL;
}
attr->type = PERF_TYPE_HARDWARE;
- attr->config = ((__u64)pmu->type << PERF_PMU_TYPE_SHIFT) | term->val.num;
+ attr->config = term->val.num;
+ if (perf_pmus__supports_extended_type())
+ attr->config |= (__u64)pmu->type << PERF_PMU_TYPE_SHIFT;
return 0;
}
if (term->type_term == PARSE_EVENTS__TERM_TYPE_USER ||
static int __parse_events_add_numeric(struct parse_events_state *parse_state,
struct list_head *list,
- struct perf_pmu *pmu, u32 type, u64 config,
- struct list_head *head_config)
+ struct perf_pmu *pmu, u32 type, u32 extended_type,
+ u64 config, struct list_head *head_config)
{
struct perf_event_attr attr;
LIST_HEAD(config_terms);
memset(&attr, 0, sizeof(attr));
attr.type = type;
attr.config = config;
+ if (extended_type && (type == PERF_TYPE_HARDWARE || type == PERF_TYPE_HW_CACHE)) {
+ assert(perf_pmus__supports_extended_type());
+ attr.config |= (u64)extended_type << PERF_PMU_TYPE_SHIFT;
+ };
if (head_config) {
if (config_attr(&attr, head_config, parse_state->error,
struct perf_pmu *pmu = NULL;
bool found_supported = false;
- if (!wildcard)
- return __parse_events_add_numeric(parse_state, list, /*pmu=*/NULL,
- type, config, head_config);
-
/* Wildcards on numeric values are only supported by core PMUs. */
- while ((pmu = perf_pmus__scan_core(pmu)) != NULL) {
- int ret;
+ if (wildcard && perf_pmus__supports_extended_type()) {
+ while ((pmu = perf_pmus__scan_core(pmu)) != NULL) {
+ int ret;
- if (parse_events__filter_pmu(parse_state, pmu))
- continue;
+ found_supported = true;
+ if (parse_events__filter_pmu(parse_state, pmu))
+ continue;
- found_supported = true;
- ret = __parse_events_add_numeric(parse_state, list, pmu, pmu->type,
- config, head_config);
- if (ret)
- return ret;
+ ret = __parse_events_add_numeric(parse_state, list, pmu,
+ type, pmu->type,
+ config, head_config);
+ if (ret)
+ return ret;
+ }
+ if (found_supported)
+ return 0;
}
- return found_supported ? 0 : -EINVAL;
+ return __parse_events_add_numeric(parse_state, list, perf_pmus__find_by_type(type),
+ type, /*extended_type=*/0, config, head_config);
}
int parse_events_add_tool(struct parse_events_state *parse_state,
{
struct evsel *leader = evsel__leader(evsel);
struct evsel *pos;
- const char *group_pmu_name = evsel->pmu_name ?: "cpu";
+ const char *group_pmu_name;
+ struct perf_pmu *pmu = evsel__find_pmu(evsel);
+ if (!pmu) {
+ /*
+ * For PERF_TYPE_HARDWARE and PERF_TYPE_HW_CACHE types the PMU
+ * is a core PMU, but in heterogeneous systems this is
+ * unknown. For now pick the first core PMU.
+ */
+ pmu = perf_pmus__scan_core(NULL);
+ }
+ if (!pmu) {
+ pr_debug("No PMU found for '%s'", evsel__name(evsel));
+ return -EINVAL;
+ }
+ group_pmu_name = pmu->name;
/*
* Software events may be in a group with other uncore PMU events. Use
* the pmu_name of the first non-software event to avoid breaking the
* Aux event leaders, like intel_pt, expect a group with events from
* other PMUs, so substitute the AUX event's PMU in this case.
*/
- if (evsel->core.attr.type == PERF_TYPE_SOFTWARE || evsel__is_aux_event(leader)) {
+ if (perf_pmu__is_software(pmu) || evsel__is_aux_event(leader)) {
+ struct perf_pmu *leader_pmu = evsel__find_pmu(leader);
+
+ if (!leader_pmu) {
+ /* As with determining pmu above. */
+ leader_pmu = perf_pmus__scan_core(NULL);
+ }
/*
* Starting with the leader, find the first event with a named
- * PMU. for_each_group_(member|evsel) isn't used as the list
- * isn't yet sorted putting evsel's in the same group together.
+ * non-software PMU. for_each_group_(member|evsel) isn't used as
+ * the list isn't yet sorted putting evsel's in the same group
+ * together.
*/
- if (leader->pmu_name) {
- group_pmu_name = leader->pmu_name;
+ if (leader_pmu && !perf_pmu__is_software(leader_pmu)) {
+ group_pmu_name = leader_pmu->name;
} else if (leader->core.nr_members > 1) {
list_for_each_entry(pos, head, core.node) {
- if (evsel__leader(pos) == leader && pos->pmu_name) {
- group_pmu_name = pos->pmu_name;
+ struct perf_pmu *pos_pmu;
+
+ if (pos == leader || evsel__leader(pos) != leader)
+ continue;
+ pos_pmu = evsel__find_pmu(pos);
+ if (!pos_pmu) {
+ /* As with determining pmu above. */
+ pos_pmu = perf_pmus__scan_core(NULL);
+ }
+ if (pos_pmu && !perf_pmu__is_software(pos_pmu)) {
+ group_pmu_name = pos_pmu->name;
break;
}
}
}
}
- evsel->group_pmu_name = strdup(group_pmu_name);
+ /* Assign the actual name taking care that the fake PMU lacks a name. */
+ evsel->group_pmu_name = strdup(group_pmu_name ?: "fake");
return evsel->group_pmu_name ? 0 : -ENOMEM;
}