linux/tools/perf/util/pmu.c
<<
>>
Prefs
   1
   2#include <linux/list.h>
   3#include <sys/types.h>
   4#include <sys/stat.h>
   5#include <unistd.h>
   6#include <stdio.h>
   7#include <dirent.h>
   8#include "sysfs.h"
   9#include "util.h"
  10#include "pmu.h"
  11#include "parse-events.h"
  12
  13int perf_pmu_parse(struct list_head *list, char *name);
  14extern FILE *perf_pmu_in;
  15
  16static LIST_HEAD(pmus);
  17
  18/*
  19 * Parse & process all the sysfs attributes located under
  20 * the directory specified in 'dir' parameter.
  21 */
  22static int pmu_format_parse(char *dir, struct list_head *head)
  23{
  24        struct dirent *evt_ent;
  25        DIR *format_dir;
  26        int ret = 0;
  27
  28        format_dir = opendir(dir);
  29        if (!format_dir)
  30                return -EINVAL;
  31
  32        while (!ret && (evt_ent = readdir(format_dir))) {
  33                char path[PATH_MAX];
  34                char *name = evt_ent->d_name;
  35                FILE *file;
  36
  37                if (!strcmp(name, ".") || !strcmp(name, ".."))
  38                        continue;
  39
  40                snprintf(path, PATH_MAX, "%s/%s", dir, name);
  41
  42                ret = -EINVAL;
  43                file = fopen(path, "r");
  44                if (!file)
  45                        break;
  46
  47                perf_pmu_in = file;
  48                ret = perf_pmu_parse(head, name);
  49                fclose(file);
  50        }
  51
  52        closedir(format_dir);
  53        return ret;
  54}
  55
  56/*
  57 * Reading/parsing the default pmu format definition, which should be
  58 * located at:
  59 * /sys/bus/event_source/devices/<dev>/format as sysfs group attributes.
  60 */
  61static int pmu_format(char *name, struct list_head *format)
  62{
  63        struct stat st;
  64        char path[PATH_MAX];
  65        const char *sysfs;
  66
  67        sysfs = sysfs_find_mountpoint();
  68        if (!sysfs)
  69                return -1;
  70
  71        snprintf(path, PATH_MAX,
  72                 "%s/bus/event_source/devices/%s/format", sysfs, name);
  73
  74        if (stat(path, &st) < 0)
  75                return -1;
  76
  77        if (pmu_format_parse(path, format))
  78                return -1;
  79
  80        return 0;
  81}
  82
  83/*
  84 * Reading/parsing the default pmu type value, which should be
  85 * located at:
  86 * /sys/bus/event_source/devices/<dev>/type as sysfs attribute.
  87 */
  88static int pmu_type(char *name, __u32 *type)
  89{
  90        struct stat st;
  91        char path[PATH_MAX];
  92        const char *sysfs;
  93        FILE *file;
  94        int ret = 0;
  95
  96        sysfs = sysfs_find_mountpoint();
  97        if (!sysfs)
  98                return -1;
  99
 100        snprintf(path, PATH_MAX,
 101                 "%s/bus/event_source/devices/%s/type", sysfs, name);
 102
 103        if (stat(path, &st) < 0)
 104                return -1;
 105
 106        file = fopen(path, "r");
 107        if (!file)
 108                return -EINVAL;
 109
 110        if (1 != fscanf(file, "%u", type))
 111                ret = -1;
 112
 113        fclose(file);
 114        return ret;
 115}
 116
 117static struct perf_pmu *pmu_lookup(char *name)
 118{
 119        struct perf_pmu *pmu;
 120        LIST_HEAD(format);
 121        __u32 type;
 122
 123        /*
 124         * The pmu data we store & need consists of the pmu
 125         * type value and format definitions. Load both right
 126         * now.
 127         */
 128        if (pmu_format(name, &format))
 129                return NULL;
 130
 131        if (pmu_type(name, &type))
 132                return NULL;
 133
 134        pmu = zalloc(sizeof(*pmu));
 135        if (!pmu)
 136                return NULL;
 137
 138        INIT_LIST_HEAD(&pmu->format);
 139        list_splice(&format, &pmu->format);
 140        pmu->name = strdup(name);
 141        pmu->type = type;
 142        return pmu;
 143}
 144
 145static struct perf_pmu *pmu_find(char *name)
 146{
 147        struct perf_pmu *pmu;
 148
 149        list_for_each_entry(pmu, &pmus, list)
 150                if (!strcmp(pmu->name, name))
 151                        return pmu;
 152
 153        return NULL;
 154}
 155
 156struct perf_pmu *perf_pmu__find(char *name)
 157{
 158        struct perf_pmu *pmu;
 159
 160        /*
 161         * Once PMU is loaded it stays in the list,
 162         * so we keep us from multiple reading/parsing
 163         * the pmu format definitions.
 164         */
 165        pmu = pmu_find(name);
 166        if (pmu)
 167                return pmu;
 168
 169        return pmu_lookup(name);
 170}
 171
 172static struct perf_pmu__format*
 173pmu_find_format(struct list_head *formats, char *name)
 174{
 175        struct perf_pmu__format *format;
 176
 177        list_for_each_entry(format, formats, list)
 178                if (!strcmp(format->name, name))
 179                        return format;
 180
 181        return NULL;
 182}
 183
 184/*
 185 * Returns value based on the format definition (format parameter)
 186 * and unformated value (value parameter).
 187 *
 188 * TODO maybe optimize a little ;)
 189 */
 190static __u64 pmu_format_value(unsigned long *format, __u64 value)
 191{
 192        unsigned long fbit, vbit;
 193        __u64 v = 0;
 194
 195        for (fbit = 0, vbit = 0; fbit < PERF_PMU_FORMAT_BITS; fbit++) {
 196
 197                if (!test_bit(fbit, format))
 198                        continue;
 199
 200                if (!(value & (1llu << vbit++)))
 201                        continue;
 202
 203                v |= (1llu << fbit);
 204        }
 205
 206        return v;
 207}
 208
 209/*
 210 * Setup one of config[12] attr members based on the
 211 * user input data - temr parameter.
 212 */
 213static int pmu_config_term(struct list_head *formats,
 214                           struct perf_event_attr *attr,
 215                           struct parse_events__term *term)
 216{
 217        struct perf_pmu__format *format;
 218        __u64 *vp;
 219
 220        /*
 221         * Support only for hardcoded and numnerial terms.
 222         * Hardcoded terms should be already in, so nothing
 223         * to be done for them.
 224         */
 225        if (parse_events__is_hardcoded_term(term))
 226                return 0;
 227
 228        if (term->type != PARSE_EVENTS__TERM_TYPE_NUM)
 229                return -EINVAL;
 230
 231        format = pmu_find_format(formats, term->config);
 232        if (!format)
 233                return -EINVAL;
 234
 235        switch (format->value) {
 236        case PERF_PMU_FORMAT_VALUE_CONFIG:
 237                vp = &attr->config;
 238                break;
 239        case PERF_PMU_FORMAT_VALUE_CONFIG1:
 240                vp = &attr->config1;
 241                break;
 242        case PERF_PMU_FORMAT_VALUE_CONFIG2:
 243                vp = &attr->config2;
 244                break;
 245        default:
 246                return -EINVAL;
 247        }
 248
 249        *vp |= pmu_format_value(format->bits, term->val.num);
 250        return 0;
 251}
 252
 253static int pmu_config(struct list_head *formats, struct perf_event_attr *attr,
 254                      struct list_head *head_terms)
 255{
 256        struct parse_events__term *term, *h;
 257
 258        list_for_each_entry_safe(term, h, head_terms, list)
 259                if (pmu_config_term(formats, attr, term))
 260                        return -EINVAL;
 261
 262        return 0;
 263}
 264
 265/*
 266 * Configures event's 'attr' parameter based on the:
 267 * 1) users input - specified in terms parameter
 268 * 2) pmu format definitions - specified by pmu parameter
 269 */
 270int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr,
 271                     struct list_head *head_terms)
 272{
 273        attr->type = pmu->type;
 274        return pmu_config(&pmu->format, attr, head_terms);
 275}
 276
 277int perf_pmu__new_format(struct list_head *list, char *name,
 278                         int config, unsigned long *bits)
 279{
 280        struct perf_pmu__format *format;
 281
 282        format = zalloc(sizeof(*format));
 283        if (!format)
 284                return -ENOMEM;
 285
 286        format->name = strdup(name);
 287        format->value = config;
 288        memcpy(format->bits, bits, sizeof(format->bits));
 289
 290        list_add_tail(&format->list, list);
 291        return 0;
 292}
 293
 294void perf_pmu__set_format(unsigned long *bits, long from, long to)
 295{
 296        long b;
 297
 298        if (!to)
 299                to = from;
 300
 301        memset(bits, 0, BITS_TO_LONGS(PERF_PMU_FORMAT_BITS));
 302        for (b = from; b <= to; b++)
 303                set_bit(b, bits);
 304}
 305
 306/* Simulated format definitions. */
 307static struct test_format {
 308        const char *name;
 309        const char *value;
 310} test_formats[] = {
 311        { "krava01", "config:0-1,62-63\n", },
 312        { "krava02", "config:10-17\n", },
 313        { "krava03", "config:5\n", },
 314        { "krava11", "config1:0,2,4,6,8,20-28\n", },
 315        { "krava12", "config1:63\n", },
 316        { "krava13", "config1:45-47\n", },
 317        { "krava21", "config2:0-3,10-13,20-23,30-33,40-43,50-53,60-63\n", },
 318        { "krava22", "config2:8,18,48,58\n", },
 319        { "krava23", "config2:28-29,38\n", },
 320};
 321
 322#define TEST_FORMATS_CNT (sizeof(test_formats) / sizeof(struct test_format))
 323
 324/* Simulated users input. */
 325static struct parse_events__term test_terms[] = {
 326        {
 327                .config  = (char *) "krava01",
 328                .val.num = 15,
 329                .type    = PARSE_EVENTS__TERM_TYPE_NUM,
 330        },
 331        {
 332                .config  = (char *) "krava02",
 333                .val.num = 170,
 334                .type    = PARSE_EVENTS__TERM_TYPE_NUM,
 335        },
 336        {
 337                .config  = (char *) "krava03",
 338                .val.num = 1,
 339                .type    = PARSE_EVENTS__TERM_TYPE_NUM,
 340        },
 341        {
 342                .config  = (char *) "krava11",
 343                .val.num = 27,
 344                .type    = PARSE_EVENTS__TERM_TYPE_NUM,
 345        },
 346        {
 347                .config  = (char *) "krava12",
 348                .val.num = 1,
 349                .type    = PARSE_EVENTS__TERM_TYPE_NUM,
 350        },
 351        {
 352                .config  = (char *) "krava13",
 353                .val.num = 2,
 354                .type    = PARSE_EVENTS__TERM_TYPE_NUM,
 355        },
 356        {
 357                .config  = (char *) "krava21",
 358                .val.num = 119,
 359                .type    = PARSE_EVENTS__TERM_TYPE_NUM,
 360        },
 361        {
 362                .config  = (char *) "krava22",
 363                .val.num = 11,
 364                .type    = PARSE_EVENTS__TERM_TYPE_NUM,
 365        },
 366        {
 367                .config  = (char *) "krava23",
 368                .val.num = 2,
 369                .type    = PARSE_EVENTS__TERM_TYPE_NUM,
 370        },
 371};
 372#define TERMS_CNT (sizeof(test_terms) / sizeof(struct parse_events__term))
 373
 374/*
 375 * Prepare format directory data, exported by kernel
 376 * at /sys/bus/event_source/devices/<dev>/format.
 377 */
 378static char *test_format_dir_get(void)
 379{
 380        static char dir[PATH_MAX];
 381        unsigned int i;
 382
 383        snprintf(dir, PATH_MAX, "/tmp/perf-pmu-test-format-XXXXXX");
 384        if (!mkdtemp(dir))
 385                return NULL;
 386
 387        for (i = 0; i < TEST_FORMATS_CNT; i++) {
 388                static char name[PATH_MAX];
 389                struct test_format *format = &test_formats[i];
 390                FILE *file;
 391
 392                snprintf(name, PATH_MAX, "%s/%s", dir, format->name);
 393
 394                file = fopen(name, "w");
 395                if (!file)
 396                        return NULL;
 397
 398                if (1 != fwrite(format->value, strlen(format->value), 1, file))
 399                        break;
 400
 401                fclose(file);
 402        }
 403
 404        return dir;
 405}
 406
 407/* Cleanup format directory. */
 408static int test_format_dir_put(char *dir)
 409{
 410        char buf[PATH_MAX];
 411        snprintf(buf, PATH_MAX, "rm -f %s/*\n", dir);
 412        if (system(buf))
 413                return -1;
 414
 415        snprintf(buf, PATH_MAX, "rmdir %s\n", dir);
 416        return system(buf);
 417}
 418
 419static struct list_head *test_terms_list(void)
 420{
 421        static LIST_HEAD(terms);
 422        unsigned int i;
 423
 424        for (i = 0; i < TERMS_CNT; i++)
 425                list_add_tail(&test_terms[i].list, &terms);
 426
 427        return &terms;
 428}
 429
 430#undef TERMS_CNT
 431
 432int perf_pmu__test(void)
 433{
 434        char *format = test_format_dir_get();
 435        LIST_HEAD(formats);
 436        struct list_head *terms = test_terms_list();
 437        int ret;
 438
 439        if (!format)
 440                return -EINVAL;
 441
 442        do {
 443                struct perf_event_attr attr;
 444
 445                memset(&attr, 0, sizeof(attr));
 446
 447                ret = pmu_format_parse(format, &formats);
 448                if (ret)
 449                        break;
 450
 451                ret = pmu_config(&formats, &attr, terms);
 452                if (ret)
 453                        break;
 454
 455                ret = -EINVAL;
 456
 457                if (attr.config  != 0xc00000000002a823)
 458                        break;
 459                if (attr.config1 != 0x8000400000000145)
 460                        break;
 461                if (attr.config2 != 0x0400000020041d07)
 462                        break;
 463
 464                ret = 0;
 465        } while (0);
 466
 467        test_format_dir_put(format);
 468        return ret;
 469}
 470