Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 #include <linux/err.h>
0003 #include <linux/zalloc.h>
0004 #include <errno.h>
0005 #include <sys/types.h>
0006 #include <sys/stat.h>
0007 #include <fcntl.h>
0008 #include <sys/param.h>
0009 #include "evlist.h"
0010 #include "evsel.h"
0011 #include "parse-events.h"
0012 #include "parse-events-hybrid.h"
0013 #include "debug.h"
0014 #include "pmu.h"
0015 #include "pmu-hybrid.h"
0016 #include "perf.h"
0017 
0018 static void config_hybrid_attr(struct perf_event_attr *attr,
0019                    int type, int pmu_type)
0020 {
0021     /*
0022      * attr.config layout for type PERF_TYPE_HARDWARE and
0023      * PERF_TYPE_HW_CACHE
0024      *
0025      * PERF_TYPE_HARDWARE:                 0xEEEEEEEE000000AA
0026      *                                     AA: hardware event ID
0027      *                                     EEEEEEEE: PMU type ID
0028      * PERF_TYPE_HW_CACHE:                 0xEEEEEEEE00DDCCBB
0029      *                                     BB: hardware cache ID
0030      *                                     CC: hardware cache op ID
0031      *                                     DD: hardware cache op result ID
0032      *                                     EEEEEEEE: PMU type ID
0033      * If the PMU type ID is 0, the PERF_TYPE_RAW will be applied.
0034      */
0035     attr->type = type;
0036     attr->config = (attr->config & PERF_HW_EVENT_MASK) |
0037             ((__u64)pmu_type << PERF_PMU_TYPE_SHIFT);
0038 }
0039 
0040 static int create_event_hybrid(__u32 config_type, int *idx,
0041                    struct list_head *list,
0042                    struct perf_event_attr *attr, const char *name,
0043                    const char *metric_id,
0044                    struct list_head *config_terms,
0045                    struct perf_pmu *pmu)
0046 {
0047     struct evsel *evsel;
0048     __u32 type = attr->type;
0049     __u64 config = attr->config;
0050 
0051     config_hybrid_attr(attr, config_type, pmu->type);
0052 
0053     /*
0054      * Some hybrid hardware cache events are only available on one CPU
0055      * PMU. For example, the 'L1-dcache-load-misses' is only available
0056      * on cpu_core, while the 'L1-icache-loads' is only available on
0057      * cpu_atom. We need to remove "not supported" hybrid cache events.
0058      */
0059     if (attr->type == PERF_TYPE_HW_CACHE
0060         && !is_event_supported(attr->type, attr->config))
0061         return 0;
0062 
0063     evsel = parse_events__add_event_hybrid(list, idx, attr, name, metric_id,
0064                            pmu, config_terms);
0065     if (evsel) {
0066         evsel->pmu_name = strdup(pmu->name);
0067         if (!evsel->pmu_name)
0068             return -ENOMEM;
0069     } else
0070         return -ENOMEM;
0071     attr->type = type;
0072     attr->config = config;
0073     return 0;
0074 }
0075 
0076 static int pmu_cmp(struct parse_events_state *parse_state,
0077            struct perf_pmu *pmu)
0078 {
0079     if (parse_state->evlist && parse_state->evlist->hybrid_pmu_name)
0080         return strcmp(parse_state->evlist->hybrid_pmu_name, pmu->name);
0081 
0082     if (parse_state->hybrid_pmu_name)
0083         return strcmp(parse_state->hybrid_pmu_name, pmu->name);
0084 
0085     return 0;
0086 }
0087 
0088 static int add_hw_hybrid(struct parse_events_state *parse_state,
0089              struct list_head *list, struct perf_event_attr *attr,
0090              const char *name, const char *metric_id,
0091              struct list_head *config_terms)
0092 {
0093     struct perf_pmu *pmu;
0094     int ret;
0095 
0096     perf_pmu__for_each_hybrid_pmu(pmu) {
0097         LIST_HEAD(terms);
0098 
0099         if (pmu_cmp(parse_state, pmu))
0100             continue;
0101 
0102         copy_config_terms(&terms, config_terms);
0103         ret = create_event_hybrid(PERF_TYPE_HARDWARE,
0104                       &parse_state->idx, list, attr, name,
0105                       metric_id, &terms, pmu);
0106         free_config_terms(&terms);
0107         if (ret)
0108             return ret;
0109     }
0110 
0111     return 0;
0112 }
0113 
0114 static int create_raw_event_hybrid(int *idx, struct list_head *list,
0115                    struct perf_event_attr *attr,
0116                    const char *name,
0117                    const char *metric_id,
0118                    struct list_head *config_terms,
0119                    struct perf_pmu *pmu)
0120 {
0121     struct evsel *evsel;
0122 
0123     attr->type = pmu->type;
0124     evsel = parse_events__add_event_hybrid(list, idx, attr, name, metric_id,
0125                            pmu, config_terms);
0126     if (evsel)
0127         evsel->pmu_name = strdup(pmu->name);
0128     else
0129         return -ENOMEM;
0130 
0131     return 0;
0132 }
0133 
0134 static int add_raw_hybrid(struct parse_events_state *parse_state,
0135               struct list_head *list, struct perf_event_attr *attr,
0136               const char *name, const char *metric_id,
0137               struct list_head *config_terms)
0138 {
0139     struct perf_pmu *pmu;
0140     int ret;
0141 
0142     perf_pmu__for_each_hybrid_pmu(pmu) {
0143         LIST_HEAD(terms);
0144 
0145         if (pmu_cmp(parse_state, pmu))
0146             continue;
0147 
0148         copy_config_terms(&terms, config_terms);
0149         ret = create_raw_event_hybrid(&parse_state->idx, list, attr,
0150                           name, metric_id, &terms, pmu);
0151         free_config_terms(&terms);
0152         if (ret)
0153             return ret;
0154     }
0155 
0156     return 0;
0157 }
0158 
0159 int parse_events__add_numeric_hybrid(struct parse_events_state *parse_state,
0160                      struct list_head *list,
0161                      struct perf_event_attr *attr,
0162                      const char *name, const char *metric_id,
0163                      struct list_head *config_terms,
0164                      bool *hybrid)
0165 {
0166     *hybrid = false;
0167     if (attr->type == PERF_TYPE_SOFTWARE)
0168         return 0;
0169 
0170     if (!perf_pmu__has_hybrid())
0171         return 0;
0172 
0173     *hybrid = true;
0174     if (attr->type != PERF_TYPE_RAW) {
0175         return add_hw_hybrid(parse_state, list, attr, name, metric_id,
0176                      config_terms);
0177     }
0178 
0179     return add_raw_hybrid(parse_state, list, attr, name, metric_id,
0180                   config_terms);
0181 }
0182 
0183 int parse_events__add_cache_hybrid(struct list_head *list, int *idx,
0184                    struct perf_event_attr *attr,
0185                    const char *name,
0186                    const char *metric_id,
0187                    struct list_head *config_terms,
0188                    bool *hybrid,
0189                    struct parse_events_state *parse_state)
0190 {
0191     struct perf_pmu *pmu;
0192     int ret;
0193 
0194     *hybrid = false;
0195     if (!perf_pmu__has_hybrid())
0196         return 0;
0197 
0198     *hybrid = true;
0199     perf_pmu__for_each_hybrid_pmu(pmu) {
0200         LIST_HEAD(terms);
0201 
0202         if (pmu_cmp(parse_state, pmu))
0203             continue;
0204 
0205         copy_config_terms(&terms, config_terms);
0206         ret = create_event_hybrid(PERF_TYPE_HW_CACHE, idx, list,
0207                       attr, name, metric_id, &terms, pmu);
0208         free_config_terms(&terms);
0209         if (ret)
0210             return ret;
0211     }
0212 
0213     return 0;
0214 }