1cd82a32eSJiri Olsa #include <linux/list.h> 2c5de47f2SSukadev Bhattiprolu #include <linux/compiler.h> 3cd82a32eSJiri Olsa #include <sys/types.h> 4cd82a32eSJiri Olsa #include <unistd.h> 5cd82a32eSJiri Olsa #include <stdio.h> 6dc0a6202SAdrian Hunter #include <stdbool.h> 77d4bdab5SAdrian Hunter #include <stdarg.h> 8cd82a32eSJiri Olsa #include <dirent.h> 9cd0cfad7SBorislav Petkov #include <api/fs/fs.h> 10410136f5SStephane Eranian #include <locale.h> 11cd82a32eSJiri Olsa #include "util.h" 12cd82a32eSJiri Olsa #include "pmu.h" 13cd82a32eSJiri Olsa #include "parse-events.h" 147ae92e74SYan, Zheng #include "cpumap.h" 15933f82ffSSukadev Bhattiprolu #include "header.h" 16933f82ffSSukadev Bhattiprolu #include "pmu-events/pmu-events.h" 1761eb2eb4SAndi Kleen #include "cache.h" 18cd82a32eSJiri Olsa 19ab1bf653SArnaldo Carvalho de Melo struct perf_pmu_format { 20ab1bf653SArnaldo Carvalho de Melo char *name; 21ab1bf653SArnaldo Carvalho de Melo int value; 22ab1bf653SArnaldo Carvalho de Melo DECLARE_BITMAP(bits, PERF_PMU_FORMAT_BITS); 23ab1bf653SArnaldo Carvalho de Melo struct list_head list; 24ab1bf653SArnaldo Carvalho de Melo }; 25ab1bf653SArnaldo Carvalho de Melo 2650a9667cSRobert Richter #define EVENT_SOURCE_DEVICE_PATH "/bus/event_source/devices/" 2750a9667cSRobert Richter 28cd82a32eSJiri Olsa int perf_pmu_parse(struct list_head *list, char *name); 29cd82a32eSJiri Olsa extern FILE *perf_pmu_in; 30cd82a32eSJiri Olsa 31cd82a32eSJiri Olsa static LIST_HEAD(pmus); 32cd82a32eSJiri Olsa 33cd82a32eSJiri Olsa /* 34cd82a32eSJiri Olsa * Parse & process all the sysfs attributes located under 35cd82a32eSJiri Olsa * the directory specified in 'dir' parameter. 36cd82a32eSJiri Olsa */ 37cff7f956SJiri Olsa int perf_pmu__format_parse(char *dir, struct list_head *head) 38cd82a32eSJiri Olsa { 39cd82a32eSJiri Olsa struct dirent *evt_ent; 40cd82a32eSJiri Olsa DIR *format_dir; 41cd82a32eSJiri Olsa int ret = 0; 42cd82a32eSJiri Olsa 43cd82a32eSJiri Olsa format_dir = opendir(dir); 44cd82a32eSJiri Olsa if (!format_dir) 45cd82a32eSJiri Olsa return -EINVAL; 46cd82a32eSJiri Olsa 47cd82a32eSJiri Olsa while (!ret && (evt_ent = readdir(format_dir))) { 48cd82a32eSJiri Olsa char path[PATH_MAX]; 49cd82a32eSJiri Olsa char *name = evt_ent->d_name; 50cd82a32eSJiri Olsa FILE *file; 51cd82a32eSJiri Olsa 52cd82a32eSJiri Olsa if (!strcmp(name, ".") || !strcmp(name, "..")) 53cd82a32eSJiri Olsa continue; 54cd82a32eSJiri Olsa 55cd82a32eSJiri Olsa snprintf(path, PATH_MAX, "%s/%s", dir, name); 56cd82a32eSJiri Olsa 57cd82a32eSJiri Olsa ret = -EINVAL; 58cd82a32eSJiri Olsa file = fopen(path, "r"); 59cd82a32eSJiri Olsa if (!file) 60cd82a32eSJiri Olsa break; 61cd82a32eSJiri Olsa 62cd82a32eSJiri Olsa perf_pmu_in = file; 63cd82a32eSJiri Olsa ret = perf_pmu_parse(head, name); 64cd82a32eSJiri Olsa fclose(file); 65cd82a32eSJiri Olsa } 66cd82a32eSJiri Olsa 67cd82a32eSJiri Olsa closedir(format_dir); 68cd82a32eSJiri Olsa return ret; 69cd82a32eSJiri Olsa } 70cd82a32eSJiri Olsa 71cd82a32eSJiri Olsa /* 72cd82a32eSJiri Olsa * Reading/parsing the default pmu format definition, which should be 73cd82a32eSJiri Olsa * located at: 74cd82a32eSJiri Olsa * /sys/bus/event_source/devices/<dev>/format as sysfs group attributes. 75cd82a32eSJiri Olsa */ 76b6b96fb4SAdrian Hunter static int pmu_format(const char *name, struct list_head *format) 77cd82a32eSJiri Olsa { 78cd82a32eSJiri Olsa struct stat st; 79cd82a32eSJiri Olsa char path[PATH_MAX]; 80cf38fadaSArnaldo Carvalho de Melo const char *sysfs = sysfs__mountpoint(); 81cd82a32eSJiri Olsa 82cd82a32eSJiri Olsa if (!sysfs) 83cd82a32eSJiri Olsa return -1; 84cd82a32eSJiri Olsa 85cd82a32eSJiri Olsa snprintf(path, PATH_MAX, 8650a9667cSRobert Richter "%s" EVENT_SOURCE_DEVICE_PATH "%s/format", sysfs, name); 87cd82a32eSJiri Olsa 88cd82a32eSJiri Olsa if (stat(path, &st) < 0) 899bc8f9feSRobert Richter return 0; /* no error if format does not exist */ 90cd82a32eSJiri Olsa 91cff7f956SJiri Olsa if (perf_pmu__format_parse(path, format)) 92cd82a32eSJiri Olsa return -1; 93cd82a32eSJiri Olsa 94cd82a32eSJiri Olsa return 0; 95cd82a32eSJiri Olsa } 96cd82a32eSJiri Olsa 97410136f5SStephane Eranian static int perf_pmu__parse_scale(struct perf_pmu_alias *alias, char *dir, char *name) 98410136f5SStephane Eranian { 99410136f5SStephane Eranian struct stat st; 100410136f5SStephane Eranian ssize_t sret; 101410136f5SStephane Eranian char scale[128]; 102410136f5SStephane Eranian int fd, ret = -1; 103410136f5SStephane Eranian char path[PATH_MAX]; 104ea8f75f9SJiri Olsa char *lc; 105410136f5SStephane Eranian 106410136f5SStephane Eranian snprintf(path, PATH_MAX, "%s/%s.scale", dir, name); 107410136f5SStephane Eranian 108410136f5SStephane Eranian fd = open(path, O_RDONLY); 109410136f5SStephane Eranian if (fd == -1) 110410136f5SStephane Eranian return -1; 111410136f5SStephane Eranian 112410136f5SStephane Eranian if (fstat(fd, &st) < 0) 113410136f5SStephane Eranian goto error; 114410136f5SStephane Eranian 115410136f5SStephane Eranian sret = read(fd, scale, sizeof(scale)-1); 116410136f5SStephane Eranian if (sret < 0) 117410136f5SStephane Eranian goto error; 118410136f5SStephane Eranian 1199ecae065SMadhavan Srinivasan if (scale[sret - 1] == '\n') 1209ecae065SMadhavan Srinivasan scale[sret - 1] = '\0'; 1219ecae065SMadhavan Srinivasan else 122410136f5SStephane Eranian scale[sret] = '\0'; 1239ecae065SMadhavan Srinivasan 124410136f5SStephane Eranian /* 125410136f5SStephane Eranian * save current locale 126410136f5SStephane Eranian */ 127410136f5SStephane Eranian lc = setlocale(LC_NUMERIC, NULL); 128410136f5SStephane Eranian 129410136f5SStephane Eranian /* 130f9a5978aSJiri Olsa * The lc string may be allocated in static storage, 131f9a5978aSJiri Olsa * so get a dynamic copy to make it survive setlocale 132f9a5978aSJiri Olsa * call below. 133f9a5978aSJiri Olsa */ 134f9a5978aSJiri Olsa lc = strdup(lc); 135f9a5978aSJiri Olsa if (!lc) { 136f9a5978aSJiri Olsa ret = -ENOMEM; 137f9a5978aSJiri Olsa goto error; 138f9a5978aSJiri Olsa } 139f9a5978aSJiri Olsa 140f9a5978aSJiri Olsa /* 141410136f5SStephane Eranian * force to C locale to ensure kernel 142410136f5SStephane Eranian * scale string is converted correctly. 143410136f5SStephane Eranian * kernel uses default C locale. 144410136f5SStephane Eranian */ 145410136f5SStephane Eranian setlocale(LC_NUMERIC, "C"); 146410136f5SStephane Eranian 147410136f5SStephane Eranian alias->scale = strtod(scale, NULL); 148410136f5SStephane Eranian 149410136f5SStephane Eranian /* restore locale */ 150410136f5SStephane Eranian setlocale(LC_NUMERIC, lc); 151410136f5SStephane Eranian 152ea8f75f9SJiri Olsa free(lc); 153f9a5978aSJiri Olsa 154410136f5SStephane Eranian ret = 0; 155410136f5SStephane Eranian error: 156410136f5SStephane Eranian close(fd); 157410136f5SStephane Eranian return ret; 158410136f5SStephane Eranian } 159410136f5SStephane Eranian 160410136f5SStephane Eranian static int perf_pmu__parse_unit(struct perf_pmu_alias *alias, char *dir, char *name) 161410136f5SStephane Eranian { 162410136f5SStephane Eranian char path[PATH_MAX]; 163410136f5SStephane Eranian ssize_t sret; 164410136f5SStephane Eranian int fd; 165410136f5SStephane Eranian 166410136f5SStephane Eranian snprintf(path, PATH_MAX, "%s/%s.unit", dir, name); 167410136f5SStephane Eranian 168410136f5SStephane Eranian fd = open(path, O_RDONLY); 169410136f5SStephane Eranian if (fd == -1) 170410136f5SStephane Eranian return -1; 171410136f5SStephane Eranian 172410136f5SStephane Eranian sret = read(fd, alias->unit, UNIT_MAX_LEN); 173410136f5SStephane Eranian if (sret < 0) 174410136f5SStephane Eranian goto error; 175410136f5SStephane Eranian 176410136f5SStephane Eranian close(fd); 177410136f5SStephane Eranian 1789ecae065SMadhavan Srinivasan if (alias->unit[sret - 1] == '\n') 1799ecae065SMadhavan Srinivasan alias->unit[sret - 1] = '\0'; 1809ecae065SMadhavan Srinivasan else 181410136f5SStephane Eranian alias->unit[sret] = '\0'; 182410136f5SStephane Eranian 183410136f5SStephane Eranian return 0; 184410136f5SStephane Eranian error: 185410136f5SStephane Eranian close(fd); 186410136f5SStephane Eranian alias->unit[0] = '\0'; 187410136f5SStephane Eranian return -1; 188410136f5SStephane Eranian } 189410136f5SStephane Eranian 190044330c1SMatt Fleming static int 191044330c1SMatt Fleming perf_pmu__parse_per_pkg(struct perf_pmu_alias *alias, char *dir, char *name) 192044330c1SMatt Fleming { 193044330c1SMatt Fleming char path[PATH_MAX]; 194044330c1SMatt Fleming int fd; 195044330c1SMatt Fleming 196044330c1SMatt Fleming snprintf(path, PATH_MAX, "%s/%s.per-pkg", dir, name); 197044330c1SMatt Fleming 198044330c1SMatt Fleming fd = open(path, O_RDONLY); 199044330c1SMatt Fleming if (fd == -1) 200044330c1SMatt Fleming return -1; 201044330c1SMatt Fleming 202044330c1SMatt Fleming close(fd); 203044330c1SMatt Fleming 204044330c1SMatt Fleming alias->per_pkg = true; 205044330c1SMatt Fleming return 0; 206044330c1SMatt Fleming } 207044330c1SMatt Fleming 2081d9e446bSJiri Olsa static int perf_pmu__parse_snapshot(struct perf_pmu_alias *alias, 2091d9e446bSJiri Olsa char *dir, char *name) 2101d9e446bSJiri Olsa { 2111d9e446bSJiri Olsa char path[PATH_MAX]; 2121d9e446bSJiri Olsa int fd; 2131d9e446bSJiri Olsa 2141d9e446bSJiri Olsa snprintf(path, PATH_MAX, "%s/%s.snapshot", dir, name); 2151d9e446bSJiri Olsa 2161d9e446bSJiri Olsa fd = open(path, O_RDONLY); 2171d9e446bSJiri Olsa if (fd == -1) 2181d9e446bSJiri Olsa return -1; 2191d9e446bSJiri Olsa 2201d9e446bSJiri Olsa alias->snapshot = true; 2211d9e446bSJiri Olsa close(fd); 2221d9e446bSJiri Olsa return 0; 2231d9e446bSJiri Olsa } 2241d9e446bSJiri Olsa 22570c646e0SSukadev Bhattiprolu static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name, 22608e60ed1SAndi Kleen char *desc, char *val) 227a6146d50SZheng Yan { 2285c6ccc37SArnaldo Carvalho de Melo struct perf_pmu_alias *alias; 229a6146d50SZheng Yan int ret; 230a6146d50SZheng Yan 231a6146d50SZheng Yan alias = malloc(sizeof(*alias)); 232a6146d50SZheng Yan if (!alias) 233a6146d50SZheng Yan return -ENOMEM; 234a6146d50SZheng Yan 235a6146d50SZheng Yan INIT_LIST_HEAD(&alias->terms); 236410136f5SStephane Eranian alias->scale = 1.0; 237410136f5SStephane Eranian alias->unit[0] = '\0'; 238044330c1SMatt Fleming alias->per_pkg = false; 23984530920SStephane Eranian alias->snapshot = false; 240410136f5SStephane Eranian 24170c646e0SSukadev Bhattiprolu ret = parse_events_terms(&alias->terms, val); 242a6146d50SZheng Yan if (ret) { 24370c646e0SSukadev Bhattiprolu pr_err("Cannot parse alias %s: %d\n", val, ret); 244a6146d50SZheng Yan free(alias); 245a6146d50SZheng Yan return ret; 246a6146d50SZheng Yan } 247a6146d50SZheng Yan 248a6146d50SZheng Yan alias->name = strdup(name); 24970c646e0SSukadev Bhattiprolu if (dir) { 250410136f5SStephane Eranian /* 251410136f5SStephane Eranian * load unit name and scale if available 252410136f5SStephane Eranian */ 253410136f5SStephane Eranian perf_pmu__parse_unit(alias, dir, name); 254410136f5SStephane Eranian perf_pmu__parse_scale(alias, dir, name); 255044330c1SMatt Fleming perf_pmu__parse_per_pkg(alias, dir, name); 2561d9e446bSJiri Olsa perf_pmu__parse_snapshot(alias, dir, name); 25770c646e0SSukadev Bhattiprolu } 258410136f5SStephane Eranian 25908e60ed1SAndi Kleen alias->desc = desc ? strdup(desc) : NULL; 26008e60ed1SAndi Kleen 261a6146d50SZheng Yan list_add_tail(&alias->list, list); 262410136f5SStephane Eranian 263a6146d50SZheng Yan return 0; 264a6146d50SZheng Yan } 265a6146d50SZheng Yan 26670c646e0SSukadev Bhattiprolu static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FILE *file) 26770c646e0SSukadev Bhattiprolu { 26870c646e0SSukadev Bhattiprolu char buf[256]; 26970c646e0SSukadev Bhattiprolu int ret; 27070c646e0SSukadev Bhattiprolu 27170c646e0SSukadev Bhattiprolu ret = fread(buf, 1, sizeof(buf), file); 27270c646e0SSukadev Bhattiprolu if (ret == 0) 27370c646e0SSukadev Bhattiprolu return -EINVAL; 27470c646e0SSukadev Bhattiprolu 27570c646e0SSukadev Bhattiprolu buf[ret] = 0; 27670c646e0SSukadev Bhattiprolu 27770c646e0SSukadev Bhattiprolu return __perf_pmu__new_alias(list, dir, name, NULL, buf); 27870c646e0SSukadev Bhattiprolu } 27970c646e0SSukadev Bhattiprolu 28046441bdcSMatt Fleming static inline bool pmu_alias_info_file(char *name) 28146441bdcSMatt Fleming { 28246441bdcSMatt Fleming size_t len; 28346441bdcSMatt Fleming 28446441bdcSMatt Fleming len = strlen(name); 28546441bdcSMatt Fleming if (len > 5 && !strcmp(name + len - 5, ".unit")) 28646441bdcSMatt Fleming return true; 28746441bdcSMatt Fleming if (len > 6 && !strcmp(name + len - 6, ".scale")) 28846441bdcSMatt Fleming return true; 289044330c1SMatt Fleming if (len > 8 && !strcmp(name + len - 8, ".per-pkg")) 290044330c1SMatt Fleming return true; 2911d9e446bSJiri Olsa if (len > 9 && !strcmp(name + len - 9, ".snapshot")) 2921d9e446bSJiri Olsa return true; 29346441bdcSMatt Fleming 29446441bdcSMatt Fleming return false; 29546441bdcSMatt Fleming } 29646441bdcSMatt Fleming 297a6146d50SZheng Yan /* 298a6146d50SZheng Yan * Process all the sysfs attributes located under the directory 299a6146d50SZheng Yan * specified in 'dir' parameter. 300a6146d50SZheng Yan */ 301a6146d50SZheng Yan static int pmu_aliases_parse(char *dir, struct list_head *head) 302a6146d50SZheng Yan { 303a6146d50SZheng Yan struct dirent *evt_ent; 304a6146d50SZheng Yan DIR *event_dir; 305a6146d50SZheng Yan 306a6146d50SZheng Yan event_dir = opendir(dir); 307a6146d50SZheng Yan if (!event_dir) 308a6146d50SZheng Yan return -EINVAL; 309a6146d50SZheng Yan 310940db6dcSAndi Kleen while ((evt_ent = readdir(event_dir))) { 311a6146d50SZheng Yan char path[PATH_MAX]; 312a6146d50SZheng Yan char *name = evt_ent->d_name; 313a6146d50SZheng Yan FILE *file; 314a6146d50SZheng Yan 315a6146d50SZheng Yan if (!strcmp(name, ".") || !strcmp(name, "..")) 316a6146d50SZheng Yan continue; 317a6146d50SZheng Yan 318410136f5SStephane Eranian /* 31946441bdcSMatt Fleming * skip info files parsed in perf_pmu__new_alias() 320410136f5SStephane Eranian */ 32146441bdcSMatt Fleming if (pmu_alias_info_file(name)) 322410136f5SStephane Eranian continue; 323410136f5SStephane Eranian 324a6146d50SZheng Yan snprintf(path, PATH_MAX, "%s/%s", dir, name); 325a6146d50SZheng Yan 326a6146d50SZheng Yan file = fopen(path, "r"); 327940db6dcSAndi Kleen if (!file) { 328940db6dcSAndi Kleen pr_debug("Cannot open %s\n", path); 329940db6dcSAndi Kleen continue; 330940db6dcSAndi Kleen } 331410136f5SStephane Eranian 332940db6dcSAndi Kleen if (perf_pmu__new_alias(head, dir, name, file) < 0) 333940db6dcSAndi Kleen pr_debug("Cannot set up %s\n", name); 334a6146d50SZheng Yan fclose(file); 335a6146d50SZheng Yan } 336a6146d50SZheng Yan 337a6146d50SZheng Yan closedir(event_dir); 338940db6dcSAndi Kleen return 0; 339a6146d50SZheng Yan } 340a6146d50SZheng Yan 341a6146d50SZheng Yan /* 342a6146d50SZheng Yan * Reading the pmu event aliases definition, which should be located at: 343a6146d50SZheng Yan * /sys/bus/event_source/devices/<dev>/events as sysfs group attributes. 344a6146d50SZheng Yan */ 345b6b96fb4SAdrian Hunter static int pmu_aliases(const char *name, struct list_head *head) 346a6146d50SZheng Yan { 347a6146d50SZheng Yan struct stat st; 348a6146d50SZheng Yan char path[PATH_MAX]; 349cf38fadaSArnaldo Carvalho de Melo const char *sysfs = sysfs__mountpoint(); 350a6146d50SZheng Yan 351a6146d50SZheng Yan if (!sysfs) 352a6146d50SZheng Yan return -1; 353a6146d50SZheng Yan 354a6146d50SZheng Yan snprintf(path, PATH_MAX, 355a6146d50SZheng Yan "%s/bus/event_source/devices/%s/events", sysfs, name); 356a6146d50SZheng Yan 357a6146d50SZheng Yan if (stat(path, &st) < 0) 3583fded963SJiri Olsa return 0; /* no error if 'events' does not exist */ 359a6146d50SZheng Yan 360a6146d50SZheng Yan if (pmu_aliases_parse(path, head)) 361a6146d50SZheng Yan return -1; 362a6146d50SZheng Yan 363a6146d50SZheng Yan return 0; 364a6146d50SZheng Yan } 365a6146d50SZheng Yan 3665c6ccc37SArnaldo Carvalho de Melo static int pmu_alias_terms(struct perf_pmu_alias *alias, 367a6146d50SZheng Yan struct list_head *terms) 368a6146d50SZheng Yan { 3697c2f8164SJiri Olsa struct parse_events_term *term, *cloned; 370a6146d50SZheng Yan LIST_HEAD(list); 371a6146d50SZheng Yan int ret; 372a6146d50SZheng Yan 373a6146d50SZheng Yan list_for_each_entry(term, &alias->terms, list) { 3747c2f8164SJiri Olsa ret = parse_events_term__clone(&cloned, term); 375a6146d50SZheng Yan if (ret) { 376682dc24cSArnaldo Carvalho de Melo parse_events_terms__purge(&list); 377a6146d50SZheng Yan return ret; 378a6146d50SZheng Yan } 3797c2f8164SJiri Olsa list_add_tail(&cloned->list, &list); 380a6146d50SZheng Yan } 381a6146d50SZheng Yan list_splice(&list, terms); 382a6146d50SZheng Yan return 0; 383a6146d50SZheng Yan } 384a6146d50SZheng Yan 385cd82a32eSJiri Olsa /* 386cd82a32eSJiri Olsa * Reading/parsing the default pmu type value, which should be 387cd82a32eSJiri Olsa * located at: 388cd82a32eSJiri Olsa * /sys/bus/event_source/devices/<dev>/type as sysfs attribute. 389cd82a32eSJiri Olsa */ 390b6b96fb4SAdrian Hunter static int pmu_type(const char *name, __u32 *type) 391cd82a32eSJiri Olsa { 392cd82a32eSJiri Olsa struct stat st; 393cd82a32eSJiri Olsa char path[PATH_MAX]; 394cd82a32eSJiri Olsa FILE *file; 395cd82a32eSJiri Olsa int ret = 0; 396cf38fadaSArnaldo Carvalho de Melo const char *sysfs = sysfs__mountpoint(); 397cd82a32eSJiri Olsa 398cd82a32eSJiri Olsa if (!sysfs) 399cd82a32eSJiri Olsa return -1; 400cd82a32eSJiri Olsa 401cd82a32eSJiri Olsa snprintf(path, PATH_MAX, 40250a9667cSRobert Richter "%s" EVENT_SOURCE_DEVICE_PATH "%s/type", sysfs, name); 403cd82a32eSJiri Olsa 404cd82a32eSJiri Olsa if (stat(path, &st) < 0) 405cd82a32eSJiri Olsa return -1; 406cd82a32eSJiri Olsa 407cd82a32eSJiri Olsa file = fopen(path, "r"); 408cd82a32eSJiri Olsa if (!file) 409cd82a32eSJiri Olsa return -EINVAL; 410cd82a32eSJiri Olsa 411cd82a32eSJiri Olsa if (1 != fscanf(file, "%u", type)) 412cd82a32eSJiri Olsa ret = -1; 413cd82a32eSJiri Olsa 414cd82a32eSJiri Olsa fclose(file); 415cd82a32eSJiri Olsa return ret; 416cd82a32eSJiri Olsa } 417cd82a32eSJiri Olsa 41850a9667cSRobert Richter /* Add all pmus in sysfs to pmu list: */ 41950a9667cSRobert Richter static void pmu_read_sysfs(void) 42050a9667cSRobert Richter { 42150a9667cSRobert Richter char path[PATH_MAX]; 42250a9667cSRobert Richter DIR *dir; 42350a9667cSRobert Richter struct dirent *dent; 424cf38fadaSArnaldo Carvalho de Melo const char *sysfs = sysfs__mountpoint(); 42550a9667cSRobert Richter 42650a9667cSRobert Richter if (!sysfs) 42750a9667cSRobert Richter return; 42850a9667cSRobert Richter 42950a9667cSRobert Richter snprintf(path, PATH_MAX, 43050a9667cSRobert Richter "%s" EVENT_SOURCE_DEVICE_PATH, sysfs); 43150a9667cSRobert Richter 43250a9667cSRobert Richter dir = opendir(path); 43350a9667cSRobert Richter if (!dir) 43450a9667cSRobert Richter return; 43550a9667cSRobert Richter 43650a9667cSRobert Richter while ((dent = readdir(dir))) { 43750a9667cSRobert Richter if (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, "..")) 43850a9667cSRobert Richter continue; 43950a9667cSRobert Richter /* add to static LIST_HEAD(pmus): */ 44050a9667cSRobert Richter perf_pmu__find(dent->d_name); 44150a9667cSRobert Richter } 44250a9667cSRobert Richter 44350a9667cSRobert Richter closedir(dir); 44450a9667cSRobert Richter } 44550a9667cSRobert Richter 446b6b96fb4SAdrian Hunter static struct cpu_map *pmu_cpumask(const char *name) 4477ae92e74SYan, Zheng { 4487ae92e74SYan, Zheng struct stat st; 4497ae92e74SYan, Zheng char path[PATH_MAX]; 4507ae92e74SYan, Zheng FILE *file; 4517ae92e74SYan, Zheng struct cpu_map *cpus; 452cf38fadaSArnaldo Carvalho de Melo const char *sysfs = sysfs__mountpoint(); 4537e3fcffeSMark Rutland const char *templates[] = { 4547e3fcffeSMark Rutland "%s/bus/event_source/devices/%s/cpumask", 4557e3fcffeSMark Rutland "%s/bus/event_source/devices/%s/cpus", 4567e3fcffeSMark Rutland NULL 4577e3fcffeSMark Rutland }; 4587e3fcffeSMark Rutland const char **template; 4597ae92e74SYan, Zheng 4607ae92e74SYan, Zheng if (!sysfs) 4617ae92e74SYan, Zheng return NULL; 4627ae92e74SYan, Zheng 4637e3fcffeSMark Rutland for (template = templates; *template; template++) { 4647e3fcffeSMark Rutland snprintf(path, PATH_MAX, *template, sysfs, name); 4657e3fcffeSMark Rutland if (stat(path, &st) == 0) 4667e3fcffeSMark Rutland break; 4677e3fcffeSMark Rutland } 4687ae92e74SYan, Zheng 4697e3fcffeSMark Rutland if (!*template) 4707ae92e74SYan, Zheng return NULL; 4717ae92e74SYan, Zheng 4727ae92e74SYan, Zheng file = fopen(path, "r"); 4737ae92e74SYan, Zheng if (!file) 4747ae92e74SYan, Zheng return NULL; 4757ae92e74SYan, Zheng 4767ae92e74SYan, Zheng cpus = cpu_map__read(file); 4777ae92e74SYan, Zheng fclose(file); 4787ae92e74SYan, Zheng return cpus; 4797ae92e74SYan, Zheng } 4807ae92e74SYan, Zheng 481933f82ffSSukadev Bhattiprolu /* 482933f82ffSSukadev Bhattiprolu * Return the CPU id as a raw string. 483933f82ffSSukadev Bhattiprolu * 484933f82ffSSukadev Bhattiprolu * Each architecture should provide a more precise id string that 485933f82ffSSukadev Bhattiprolu * can be use to match the architecture's "mapfile". 486933f82ffSSukadev Bhattiprolu */ 487933f82ffSSukadev Bhattiprolu char * __weak get_cpuid_str(void) 488933f82ffSSukadev Bhattiprolu { 489933f82ffSSukadev Bhattiprolu return NULL; 490933f82ffSSukadev Bhattiprolu } 491933f82ffSSukadev Bhattiprolu 492933f82ffSSukadev Bhattiprolu /* 493933f82ffSSukadev Bhattiprolu * From the pmu_events_map, find the table of PMU events that corresponds 494933f82ffSSukadev Bhattiprolu * to the current running CPU. Then, add all PMU events from that table 495933f82ffSSukadev Bhattiprolu * as aliases. 496933f82ffSSukadev Bhattiprolu */ 497933f82ffSSukadev Bhattiprolu static void pmu_add_cpu_aliases(struct list_head *head) 498933f82ffSSukadev Bhattiprolu { 499933f82ffSSukadev Bhattiprolu int i; 500933f82ffSSukadev Bhattiprolu struct pmu_events_map *map; 501933f82ffSSukadev Bhattiprolu struct pmu_event *pe; 502933f82ffSSukadev Bhattiprolu char *cpuid; 503933f82ffSSukadev Bhattiprolu 504fc06e2a5SAndi Kleen cpuid = getenv("PERF_CPUID"); 505fc06e2a5SAndi Kleen if (cpuid) 506fc06e2a5SAndi Kleen cpuid = strdup(cpuid); 507fc06e2a5SAndi Kleen if (!cpuid) 508933f82ffSSukadev Bhattiprolu cpuid = get_cpuid_str(); 509933f82ffSSukadev Bhattiprolu if (!cpuid) 510933f82ffSSukadev Bhattiprolu return; 511933f82ffSSukadev Bhattiprolu 512fc06e2a5SAndi Kleen pr_debug("Using CPUID %s\n", cpuid); 513fc06e2a5SAndi Kleen 514933f82ffSSukadev Bhattiprolu i = 0; 515933f82ffSSukadev Bhattiprolu while (1) { 516933f82ffSSukadev Bhattiprolu map = &pmu_events_map[i++]; 517933f82ffSSukadev Bhattiprolu if (!map->table) 518933f82ffSSukadev Bhattiprolu goto out; 519933f82ffSSukadev Bhattiprolu 520933f82ffSSukadev Bhattiprolu if (!strcmp(map->cpuid, cpuid)) 521933f82ffSSukadev Bhattiprolu break; 522933f82ffSSukadev Bhattiprolu } 523933f82ffSSukadev Bhattiprolu 524933f82ffSSukadev Bhattiprolu /* 525933f82ffSSukadev Bhattiprolu * Found a matching PMU events table. Create aliases 526933f82ffSSukadev Bhattiprolu */ 527933f82ffSSukadev Bhattiprolu i = 0; 528933f82ffSSukadev Bhattiprolu while (1) { 529933f82ffSSukadev Bhattiprolu pe = &map->table[i++]; 530933f82ffSSukadev Bhattiprolu if (!pe->name) 531933f82ffSSukadev Bhattiprolu break; 532933f82ffSSukadev Bhattiprolu 533933f82ffSSukadev Bhattiprolu /* need type casts to override 'const' */ 534933f82ffSSukadev Bhattiprolu __perf_pmu__new_alias(head, NULL, (char *)pe->name, 535933f82ffSSukadev Bhattiprolu (char *)pe->desc, (char *)pe->event); 536933f82ffSSukadev Bhattiprolu } 537933f82ffSSukadev Bhattiprolu 538933f82ffSSukadev Bhattiprolu out: 539933f82ffSSukadev Bhattiprolu free(cpuid); 540933f82ffSSukadev Bhattiprolu } 541933f82ffSSukadev Bhattiprolu 542c5de47f2SSukadev Bhattiprolu struct perf_event_attr * __weak 543dc0a6202SAdrian Hunter perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused) 544dc0a6202SAdrian Hunter { 545dc0a6202SAdrian Hunter return NULL; 546dc0a6202SAdrian Hunter } 547dc0a6202SAdrian Hunter 548b6b96fb4SAdrian Hunter static struct perf_pmu *pmu_lookup(const char *name) 549cd82a32eSJiri Olsa { 550cd82a32eSJiri Olsa struct perf_pmu *pmu; 551cd82a32eSJiri Olsa LIST_HEAD(format); 552a6146d50SZheng Yan LIST_HEAD(aliases); 553cd82a32eSJiri Olsa __u32 type; 554cd82a32eSJiri Olsa 555cd82a32eSJiri Olsa /* 556cd82a32eSJiri Olsa * The pmu data we store & need consists of the pmu 557cd82a32eSJiri Olsa * type value and format definitions. Load both right 558cd82a32eSJiri Olsa * now. 559cd82a32eSJiri Olsa */ 560cd82a32eSJiri Olsa if (pmu_format(name, &format)) 561cd82a32eSJiri Olsa return NULL; 562cd82a32eSJiri Olsa 5633fded963SJiri Olsa if (pmu_aliases(name, &aliases)) 5643fded963SJiri Olsa return NULL; 5653fded963SJiri Olsa 566933f82ffSSukadev Bhattiprolu if (!strcmp(name, "cpu")) 567933f82ffSSukadev Bhattiprolu pmu_add_cpu_aliases(&aliases); 568933f82ffSSukadev Bhattiprolu 569cd82a32eSJiri Olsa if (pmu_type(name, &type)) 570cd82a32eSJiri Olsa return NULL; 571cd82a32eSJiri Olsa 572cd82a32eSJiri Olsa pmu = zalloc(sizeof(*pmu)); 573cd82a32eSJiri Olsa if (!pmu) 574cd82a32eSJiri Olsa return NULL; 575cd82a32eSJiri Olsa 5767ae92e74SYan, Zheng pmu->cpus = pmu_cpumask(name); 5777ae92e74SYan, Zheng 578cd82a32eSJiri Olsa INIT_LIST_HEAD(&pmu->format); 579a6146d50SZheng Yan INIT_LIST_HEAD(&pmu->aliases); 580cd82a32eSJiri Olsa list_splice(&format, &pmu->format); 581a6146d50SZheng Yan list_splice(&aliases, &pmu->aliases); 582cd82a32eSJiri Olsa pmu->name = strdup(name); 583cd82a32eSJiri Olsa pmu->type = type; 5849bc8f9feSRobert Richter list_add_tail(&pmu->list, &pmus); 585dc0a6202SAdrian Hunter 586dc0a6202SAdrian Hunter pmu->default_config = perf_pmu__get_default_config(pmu); 587dc0a6202SAdrian Hunter 588cd82a32eSJiri Olsa return pmu; 589cd82a32eSJiri Olsa } 590cd82a32eSJiri Olsa 591b6b96fb4SAdrian Hunter static struct perf_pmu *pmu_find(const char *name) 592cd82a32eSJiri Olsa { 593cd82a32eSJiri Olsa struct perf_pmu *pmu; 594cd82a32eSJiri Olsa 595cd82a32eSJiri Olsa list_for_each_entry(pmu, &pmus, list) 596cd82a32eSJiri Olsa if (!strcmp(pmu->name, name)) 597cd82a32eSJiri Olsa return pmu; 598cd82a32eSJiri Olsa 599cd82a32eSJiri Olsa return NULL; 600cd82a32eSJiri Olsa } 601cd82a32eSJiri Olsa 60250a9667cSRobert Richter struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu) 60350a9667cSRobert Richter { 60450a9667cSRobert Richter /* 60550a9667cSRobert Richter * pmu iterator: If pmu is NULL, we start at the begin, 60650a9667cSRobert Richter * otherwise return the next pmu. Returns NULL on end. 60750a9667cSRobert Richter */ 60850a9667cSRobert Richter if (!pmu) { 60950a9667cSRobert Richter pmu_read_sysfs(); 61050a9667cSRobert Richter pmu = list_prepare_entry(pmu, &pmus, list); 61150a9667cSRobert Richter } 61250a9667cSRobert Richter list_for_each_entry_continue(pmu, &pmus, list) 61350a9667cSRobert Richter return pmu; 61450a9667cSRobert Richter return NULL; 61550a9667cSRobert Richter } 61650a9667cSRobert Richter 617b6b96fb4SAdrian Hunter struct perf_pmu *perf_pmu__find(const char *name) 618cd82a32eSJiri Olsa { 619cd82a32eSJiri Olsa struct perf_pmu *pmu; 620cd82a32eSJiri Olsa 621cd82a32eSJiri Olsa /* 622cd82a32eSJiri Olsa * Once PMU is loaded it stays in the list, 623cd82a32eSJiri Olsa * so we keep us from multiple reading/parsing 624cd82a32eSJiri Olsa * the pmu format definitions. 625cd82a32eSJiri Olsa */ 626cd82a32eSJiri Olsa pmu = pmu_find(name); 627cd82a32eSJiri Olsa if (pmu) 628cd82a32eSJiri Olsa return pmu; 629cd82a32eSJiri Olsa 630cd82a32eSJiri Olsa return pmu_lookup(name); 631cd82a32eSJiri Olsa } 632cd82a32eSJiri Olsa 6335c6ccc37SArnaldo Carvalho de Melo static struct perf_pmu_format * 63409ff6071SAdrian Hunter pmu_find_format(struct list_head *formats, const char *name) 635cd82a32eSJiri Olsa { 6365c6ccc37SArnaldo Carvalho de Melo struct perf_pmu_format *format; 637cd82a32eSJiri Olsa 638cd82a32eSJiri Olsa list_for_each_entry(format, formats, list) 639cd82a32eSJiri Olsa if (!strcmp(format->name, name)) 640cd82a32eSJiri Olsa return format; 641cd82a32eSJiri Olsa 642cd82a32eSJiri Olsa return NULL; 643cd82a32eSJiri Olsa } 644cd82a32eSJiri Olsa 64509ff6071SAdrian Hunter __u64 perf_pmu__format_bits(struct list_head *formats, const char *name) 64609ff6071SAdrian Hunter { 64709ff6071SAdrian Hunter struct perf_pmu_format *format = pmu_find_format(formats, name); 64809ff6071SAdrian Hunter __u64 bits = 0; 64909ff6071SAdrian Hunter int fbit; 65009ff6071SAdrian Hunter 65109ff6071SAdrian Hunter if (!format) 65209ff6071SAdrian Hunter return 0; 65309ff6071SAdrian Hunter 65409ff6071SAdrian Hunter for_each_set_bit(fbit, format->bits, PERF_PMU_FORMAT_BITS) 65509ff6071SAdrian Hunter bits |= 1ULL << fbit; 65609ff6071SAdrian Hunter 65709ff6071SAdrian Hunter return bits; 65809ff6071SAdrian Hunter } 65909ff6071SAdrian Hunter 660cd82a32eSJiri Olsa /* 661dc0a6202SAdrian Hunter * Sets value based on the format definition (format parameter) 662cd82a32eSJiri Olsa * and unformated value (value parameter). 663cd82a32eSJiri Olsa */ 664dc0a6202SAdrian Hunter static void pmu_format_value(unsigned long *format, __u64 value, __u64 *v, 665dc0a6202SAdrian Hunter bool zero) 666cd82a32eSJiri Olsa { 667cd82a32eSJiri Olsa unsigned long fbit, vbit; 668cd82a32eSJiri Olsa 669cd82a32eSJiri Olsa for (fbit = 0, vbit = 0; fbit < PERF_PMU_FORMAT_BITS; fbit++) { 670cd82a32eSJiri Olsa 671cd82a32eSJiri Olsa if (!test_bit(fbit, format)) 672cd82a32eSJiri Olsa continue; 673cd82a32eSJiri Olsa 674dc0a6202SAdrian Hunter if (value & (1llu << vbit++)) 675dc0a6202SAdrian Hunter *v |= (1llu << fbit); 676dc0a6202SAdrian Hunter else if (zero) 677dc0a6202SAdrian Hunter *v &= ~(1llu << fbit); 678cd82a32eSJiri Olsa } 679cd82a32eSJiri Olsa } 680cd82a32eSJiri Olsa 6810efe6b67SAdrian Hunter static __u64 pmu_format_max_value(const unsigned long *format) 6820efe6b67SAdrian Hunter { 683ac0e2cd5SKan Liang __u64 w = 0; 684ac0e2cd5SKan Liang int fbit; 6850efe6b67SAdrian Hunter 686ac0e2cd5SKan Liang for_each_set_bit(fbit, format, PERF_PMU_FORMAT_BITS) 687ac0e2cd5SKan Liang w |= (1ULL << fbit); 688ac0e2cd5SKan Liang 689ac0e2cd5SKan Liang return w; 6900efe6b67SAdrian Hunter } 6910efe6b67SAdrian Hunter 692cd82a32eSJiri Olsa /* 693688d4dfcSCody P Schafer * Term is a string term, and might be a param-term. Try to look up it's value 694688d4dfcSCody P Schafer * in the remaining terms. 695688d4dfcSCody P Schafer * - We have a term like "base-or-format-term=param-term", 696688d4dfcSCody P Schafer * - We need to find the value supplied for "param-term" (with param-term named 697688d4dfcSCody P Schafer * in a config string) later on in the term list. 698688d4dfcSCody P Schafer */ 699688d4dfcSCody P Schafer static int pmu_resolve_param_term(struct parse_events_term *term, 700688d4dfcSCody P Schafer struct list_head *head_terms, 701688d4dfcSCody P Schafer __u64 *value) 702688d4dfcSCody P Schafer { 703688d4dfcSCody P Schafer struct parse_events_term *t; 704688d4dfcSCody P Schafer 705688d4dfcSCody P Schafer list_for_each_entry(t, head_terms, list) { 706688d4dfcSCody P Schafer if (t->type_val == PARSE_EVENTS__TERM_TYPE_NUM) { 707688d4dfcSCody P Schafer if (!strcmp(t->config, term->config)) { 708688d4dfcSCody P Schafer t->used = true; 709688d4dfcSCody P Schafer *value = t->val.num; 710688d4dfcSCody P Schafer return 0; 711688d4dfcSCody P Schafer } 712688d4dfcSCody P Schafer } 713688d4dfcSCody P Schafer } 714688d4dfcSCody P Schafer 715688d4dfcSCody P Schafer if (verbose) 716688d4dfcSCody P Schafer printf("Required parameter '%s' not specified\n", term->config); 717688d4dfcSCody P Schafer 718688d4dfcSCody P Schafer return -1; 719688d4dfcSCody P Schafer } 720688d4dfcSCody P Schafer 721ffeb883eSHe Kuang static char *pmu_formats_string(struct list_head *formats) 722e64b020bSJiri Olsa { 723e64b020bSJiri Olsa struct perf_pmu_format *format; 72411db4e29SMasami Hiramatsu char *str = NULL; 72511db4e29SMasami Hiramatsu struct strbuf buf = STRBUF_INIT; 726e64b020bSJiri Olsa unsigned i = 0; 727e64b020bSJiri Olsa 728ffeb883eSHe Kuang if (!formats) 729e64b020bSJiri Olsa return NULL; 730e64b020bSJiri Olsa 731e64b020bSJiri Olsa /* sysfs exported terms */ 732ffeb883eSHe Kuang list_for_each_entry(format, formats, list) 73311db4e29SMasami Hiramatsu if (strbuf_addf(&buf, i++ ? ",%s" : "%s", format->name) < 0) 73411db4e29SMasami Hiramatsu goto error; 735e64b020bSJiri Olsa 736ffeb883eSHe Kuang str = strbuf_detach(&buf, NULL); 73711db4e29SMasami Hiramatsu error: 738ffeb883eSHe Kuang strbuf_release(&buf); 739e64b020bSJiri Olsa 740e64b020bSJiri Olsa return str; 741e64b020bSJiri Olsa } 742e64b020bSJiri Olsa 743688d4dfcSCody P Schafer /* 744cd82a32eSJiri Olsa * Setup one of config[12] attr members based on the 74588aca8d9SCody P Schafer * user input data - term parameter. 746cd82a32eSJiri Olsa */ 747cd82a32eSJiri Olsa static int pmu_config_term(struct list_head *formats, 748cd82a32eSJiri Olsa struct perf_event_attr *attr, 749dc0a6202SAdrian Hunter struct parse_events_term *term, 750688d4dfcSCody P Schafer struct list_head *head_terms, 751e64b020bSJiri Olsa bool zero, struct parse_events_error *err) 752cd82a32eSJiri Olsa { 7535c6ccc37SArnaldo Carvalho de Melo struct perf_pmu_format *format; 754cd82a32eSJiri Olsa __u64 *vp; 7550efe6b67SAdrian Hunter __u64 val, max_val; 756cd82a32eSJiri Olsa 757cd82a32eSJiri Olsa /* 758688d4dfcSCody P Schafer * If this is a parameter we've already used for parameterized-eval, 759688d4dfcSCody P Schafer * skip it in normal eval. 760688d4dfcSCody P Schafer */ 761688d4dfcSCody P Schafer if (term->used) 762688d4dfcSCody P Schafer return 0; 763688d4dfcSCody P Schafer 764688d4dfcSCody P Schafer /* 765cd82a32eSJiri Olsa * Hardcoded terms should be already in, so nothing 766cd82a32eSJiri Olsa * to be done for them. 767cd82a32eSJiri Olsa */ 768cd82a32eSJiri Olsa if (parse_events__is_hardcoded_term(term)) 769cd82a32eSJiri Olsa return 0; 770cd82a32eSJiri Olsa 771cd82a32eSJiri Olsa format = pmu_find_format(formats, term->config); 772688d4dfcSCody P Schafer if (!format) { 773688d4dfcSCody P Schafer if (verbose) 774688d4dfcSCody P Schafer printf("Invalid event/parameter '%s'\n", term->config); 775e64b020bSJiri Olsa if (err) { 776ffeb883eSHe Kuang char *pmu_term = pmu_formats_string(formats); 777ffeb883eSHe Kuang 778e64b020bSJiri Olsa err->idx = term->err_term; 779e64b020bSJiri Olsa err->str = strdup("unknown term"); 780ffeb883eSHe Kuang err->help = parse_events_formats_error_string(pmu_term); 781ffeb883eSHe Kuang free(pmu_term); 782e64b020bSJiri Olsa } 783cd82a32eSJiri Olsa return -EINVAL; 784688d4dfcSCody P Schafer } 785cd82a32eSJiri Olsa 786cd82a32eSJiri Olsa switch (format->value) { 787cd82a32eSJiri Olsa case PERF_PMU_FORMAT_VALUE_CONFIG: 788cd82a32eSJiri Olsa vp = &attr->config; 789cd82a32eSJiri Olsa break; 790cd82a32eSJiri Olsa case PERF_PMU_FORMAT_VALUE_CONFIG1: 791cd82a32eSJiri Olsa vp = &attr->config1; 792cd82a32eSJiri Olsa break; 793cd82a32eSJiri Olsa case PERF_PMU_FORMAT_VALUE_CONFIG2: 794cd82a32eSJiri Olsa vp = &attr->config2; 795cd82a32eSJiri Olsa break; 796cd82a32eSJiri Olsa default: 797cd82a32eSJiri Olsa return -EINVAL; 798cd82a32eSJiri Olsa } 799cd82a32eSJiri Olsa 80016fa7e82SJiri Olsa /* 801688d4dfcSCody P Schafer * Either directly use a numeric term, or try to translate string terms 802688d4dfcSCody P Schafer * using event parameters. 80316fa7e82SJiri Olsa */ 804688d4dfcSCody P Schafer if (term->type_val == PARSE_EVENTS__TERM_TYPE_NUM) 805688d4dfcSCody P Schafer val = term->val.num; 806688d4dfcSCody P Schafer else if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR) { 807688d4dfcSCody P Schafer if (strcmp(term->val.str, "?")) { 808e64b020bSJiri Olsa if (verbose) { 809688d4dfcSCody P Schafer pr_info("Invalid sysfs entry %s=%s\n", 810688d4dfcSCody P Schafer term->config, term->val.str); 811e64b020bSJiri Olsa } 812e64b020bSJiri Olsa if (err) { 813e64b020bSJiri Olsa err->idx = term->err_val; 814e64b020bSJiri Olsa err->str = strdup("expected numeric value"); 815e64b020bSJiri Olsa } 816688d4dfcSCody P Schafer return -EINVAL; 817688d4dfcSCody P Schafer } 818688d4dfcSCody P Schafer 819688d4dfcSCody P Schafer if (pmu_resolve_param_term(term, head_terms, &val)) 820688d4dfcSCody P Schafer return -EINVAL; 821688d4dfcSCody P Schafer } else 822688d4dfcSCody P Schafer return -EINVAL; 823688d4dfcSCody P Schafer 8240efe6b67SAdrian Hunter max_val = pmu_format_max_value(format->bits); 8250efe6b67SAdrian Hunter if (val > max_val) { 8260efe6b67SAdrian Hunter if (err) { 8270efe6b67SAdrian Hunter err->idx = term->err_val; 8280efe6b67SAdrian Hunter if (asprintf(&err->str, 8290efe6b67SAdrian Hunter "value too big for format, maximum is %llu", 8300efe6b67SAdrian Hunter (unsigned long long)max_val) < 0) 8310efe6b67SAdrian Hunter err->str = strdup("value too big for format"); 8320efe6b67SAdrian Hunter return -EINVAL; 8330efe6b67SAdrian Hunter } 8340efe6b67SAdrian Hunter /* 8350efe6b67SAdrian Hunter * Assume we don't care if !err, in which case the value will be 8360efe6b67SAdrian Hunter * silently truncated. 8370efe6b67SAdrian Hunter */ 8380efe6b67SAdrian Hunter } 8390efe6b67SAdrian Hunter 840688d4dfcSCody P Schafer pmu_format_value(format->bits, val, vp, zero); 841cd82a32eSJiri Olsa return 0; 842cd82a32eSJiri Olsa } 843cd82a32eSJiri Olsa 844cff7f956SJiri Olsa int perf_pmu__config_terms(struct list_head *formats, 845cff7f956SJiri Olsa struct perf_event_attr *attr, 846dc0a6202SAdrian Hunter struct list_head *head_terms, 847e64b020bSJiri Olsa bool zero, struct parse_events_error *err) 848cd82a32eSJiri Olsa { 8496cee6cd3SArnaldo Carvalho de Melo struct parse_events_term *term; 850cd82a32eSJiri Olsa 851688d4dfcSCody P Schafer list_for_each_entry(term, head_terms, list) { 852e64b020bSJiri Olsa if (pmu_config_term(formats, attr, term, head_terms, 853e64b020bSJiri Olsa zero, err)) 854cd82a32eSJiri Olsa return -EINVAL; 855688d4dfcSCody P Schafer } 856cd82a32eSJiri Olsa 857cd82a32eSJiri Olsa return 0; 858cd82a32eSJiri Olsa } 859cd82a32eSJiri Olsa 860cd82a32eSJiri Olsa /* 861cd82a32eSJiri Olsa * Configures event's 'attr' parameter based on the: 862cd82a32eSJiri Olsa * 1) users input - specified in terms parameter 863cd82a32eSJiri Olsa * 2) pmu format definitions - specified by pmu parameter 864cd82a32eSJiri Olsa */ 865cd82a32eSJiri Olsa int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, 866e64b020bSJiri Olsa struct list_head *head_terms, 867e64b020bSJiri Olsa struct parse_events_error *err) 868cd82a32eSJiri Olsa { 869dc0a6202SAdrian Hunter bool zero = !!pmu->default_config; 870dc0a6202SAdrian Hunter 871cd82a32eSJiri Olsa attr->type = pmu->type; 872e64b020bSJiri Olsa return perf_pmu__config_terms(&pmu->format, attr, head_terms, 873e64b020bSJiri Olsa zero, err); 874cd82a32eSJiri Olsa } 875cd82a32eSJiri Olsa 8765c6ccc37SArnaldo Carvalho de Melo static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu, 8776cee6cd3SArnaldo Carvalho de Melo struct parse_events_term *term) 878a6146d50SZheng Yan { 8795c6ccc37SArnaldo Carvalho de Melo struct perf_pmu_alias *alias; 880a6146d50SZheng Yan char *name; 881a6146d50SZheng Yan 882a6146d50SZheng Yan if (parse_events__is_hardcoded_term(term)) 883a6146d50SZheng Yan return NULL; 884a6146d50SZheng Yan 885a6146d50SZheng Yan if (term->type_val == PARSE_EVENTS__TERM_TYPE_NUM) { 886a6146d50SZheng Yan if (term->val.num != 1) 887a6146d50SZheng Yan return NULL; 888a6146d50SZheng Yan if (pmu_find_format(&pmu->format, term->config)) 889a6146d50SZheng Yan return NULL; 890a6146d50SZheng Yan name = term->config; 891a6146d50SZheng Yan } else if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR) { 892a6146d50SZheng Yan if (strcasecmp(term->config, "event")) 893a6146d50SZheng Yan return NULL; 894a6146d50SZheng Yan name = term->val.str; 895a6146d50SZheng Yan } else { 896a6146d50SZheng Yan return NULL; 897a6146d50SZheng Yan } 898a6146d50SZheng Yan 899a6146d50SZheng Yan list_for_each_entry(alias, &pmu->aliases, list) { 900a6146d50SZheng Yan if (!strcasecmp(alias->name, name)) 901a6146d50SZheng Yan return alias; 902a6146d50SZheng Yan } 903a6146d50SZheng Yan return NULL; 904a6146d50SZheng Yan } 905a6146d50SZheng Yan 906410136f5SStephane Eranian 9071d9e446bSJiri Olsa static int check_info_data(struct perf_pmu_alias *alias, 9081d9e446bSJiri Olsa struct perf_pmu_info *info) 909410136f5SStephane Eranian { 910410136f5SStephane Eranian /* 911410136f5SStephane Eranian * Only one term in event definition can 9121d9e446bSJiri Olsa * define unit, scale and snapshot, fail 9131d9e446bSJiri Olsa * if there's more than one. 914410136f5SStephane Eranian */ 9151d9e446bSJiri Olsa if ((info->unit && alias->unit) || 9161d9e446bSJiri Olsa (info->scale && alias->scale) || 9171d9e446bSJiri Olsa (info->snapshot && alias->snapshot)) 918410136f5SStephane Eranian return -EINVAL; 919410136f5SStephane Eranian 920410136f5SStephane Eranian if (alias->unit) 9211d9e446bSJiri Olsa info->unit = alias->unit; 922410136f5SStephane Eranian 923410136f5SStephane Eranian if (alias->scale) 9241d9e446bSJiri Olsa info->scale = alias->scale; 9251d9e446bSJiri Olsa 9261d9e446bSJiri Olsa if (alias->snapshot) 9271d9e446bSJiri Olsa info->snapshot = alias->snapshot; 928410136f5SStephane Eranian 929410136f5SStephane Eranian return 0; 930410136f5SStephane Eranian } 931410136f5SStephane Eranian 932a6146d50SZheng Yan /* 933a6146d50SZheng Yan * Find alias in the terms list and replace it with the terms 934a6146d50SZheng Yan * defined for the alias 935a6146d50SZheng Yan */ 936410136f5SStephane Eranian int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, 93746441bdcSMatt Fleming struct perf_pmu_info *info) 938a6146d50SZheng Yan { 9396cee6cd3SArnaldo Carvalho de Melo struct parse_events_term *term, *h; 9405c6ccc37SArnaldo Carvalho de Melo struct perf_pmu_alias *alias; 941a6146d50SZheng Yan int ret; 942a6146d50SZheng Yan 943044330c1SMatt Fleming info->per_pkg = false; 944044330c1SMatt Fleming 9458a398897SStephane Eranian /* 9468a398897SStephane Eranian * Mark unit and scale as not set 9478a398897SStephane Eranian * (different from default values, see below) 9488a398897SStephane Eranian */ 94946441bdcSMatt Fleming info->unit = NULL; 95046441bdcSMatt Fleming info->scale = 0.0; 9511d9e446bSJiri Olsa info->snapshot = false; 952410136f5SStephane Eranian 953a6146d50SZheng Yan list_for_each_entry_safe(term, h, head_terms, list) { 954a6146d50SZheng Yan alias = pmu_find_alias(pmu, term); 955a6146d50SZheng Yan if (!alias) 956a6146d50SZheng Yan continue; 957a6146d50SZheng Yan ret = pmu_alias_terms(alias, &term->list); 958a6146d50SZheng Yan if (ret) 959a6146d50SZheng Yan return ret; 960410136f5SStephane Eranian 9611d9e446bSJiri Olsa ret = check_info_data(alias, info); 962410136f5SStephane Eranian if (ret) 963410136f5SStephane Eranian return ret; 964410136f5SStephane Eranian 965044330c1SMatt Fleming if (alias->per_pkg) 966044330c1SMatt Fleming info->per_pkg = true; 967044330c1SMatt Fleming 968a6146d50SZheng Yan list_del(&term->list); 969a6146d50SZheng Yan free(term); 970a6146d50SZheng Yan } 9718a398897SStephane Eranian 9728a398897SStephane Eranian /* 9738a398897SStephane Eranian * if no unit or scale foundin aliases, then 9748a398897SStephane Eranian * set defaults as for evsel 9758a398897SStephane Eranian * unit cannot left to NULL 9768a398897SStephane Eranian */ 97746441bdcSMatt Fleming if (info->unit == NULL) 97846441bdcSMatt Fleming info->unit = ""; 9798a398897SStephane Eranian 98046441bdcSMatt Fleming if (info->scale == 0.0) 98146441bdcSMatt Fleming info->scale = 1.0; 9828a398897SStephane Eranian 983a6146d50SZheng Yan return 0; 984a6146d50SZheng Yan } 985a6146d50SZheng Yan 986cd82a32eSJiri Olsa int perf_pmu__new_format(struct list_head *list, char *name, 987cd82a32eSJiri Olsa int config, unsigned long *bits) 988cd82a32eSJiri Olsa { 9895c6ccc37SArnaldo Carvalho de Melo struct perf_pmu_format *format; 990cd82a32eSJiri Olsa 991cd82a32eSJiri Olsa format = zalloc(sizeof(*format)); 992cd82a32eSJiri Olsa if (!format) 993cd82a32eSJiri Olsa return -ENOMEM; 994cd82a32eSJiri Olsa 995cd82a32eSJiri Olsa format->name = strdup(name); 996cd82a32eSJiri Olsa format->value = config; 997cd82a32eSJiri Olsa memcpy(format->bits, bits, sizeof(format->bits)); 998cd82a32eSJiri Olsa 999cd82a32eSJiri Olsa list_add_tail(&format->list, list); 1000cd82a32eSJiri Olsa return 0; 1001cd82a32eSJiri Olsa } 1002cd82a32eSJiri Olsa 1003cd82a32eSJiri Olsa void perf_pmu__set_format(unsigned long *bits, long from, long to) 1004cd82a32eSJiri Olsa { 1005cd82a32eSJiri Olsa long b; 1006cd82a32eSJiri Olsa 1007cd82a32eSJiri Olsa if (!to) 1008cd82a32eSJiri Olsa to = from; 1009cd82a32eSJiri Olsa 101015268138SSukadev Bhattiprolu memset(bits, 0, BITS_TO_BYTES(PERF_PMU_FORMAT_BITS)); 1011cd82a32eSJiri Olsa for (b = from; b <= to; b++) 1012cd82a32eSJiri Olsa set_bit(b, bits); 1013cd82a32eSJiri Olsa } 1014dc098b35SAndi Kleen 1015aaea3617SCody P Schafer static int sub_non_neg(int a, int b) 1016aaea3617SCody P Schafer { 1017aaea3617SCody P Schafer if (b > a) 1018aaea3617SCody P Schafer return 0; 1019aaea3617SCody P Schafer return a - b; 1020aaea3617SCody P Schafer } 1021aaea3617SCody P Schafer 1022dc098b35SAndi Kleen static char *format_alias(char *buf, int len, struct perf_pmu *pmu, 1023dc098b35SAndi Kleen struct perf_pmu_alias *alias) 1024dc098b35SAndi Kleen { 1025aaea3617SCody P Schafer struct parse_events_term *term; 1026aaea3617SCody P Schafer int used = snprintf(buf, len, "%s/%s", pmu->name, alias->name); 1027aaea3617SCody P Schafer 1028aaea3617SCody P Schafer list_for_each_entry(term, &alias->terms, list) { 1029aaea3617SCody P Schafer if (term->type_val == PARSE_EVENTS__TERM_TYPE_STR) 1030aaea3617SCody P Schafer used += snprintf(buf + used, sub_non_neg(len, used), 1031aaea3617SCody P Schafer ",%s=%s", term->config, 1032aaea3617SCody P Schafer term->val.str); 1033aaea3617SCody P Schafer } 1034aaea3617SCody P Schafer 1035aaea3617SCody P Schafer if (sub_non_neg(len, used) > 0) { 1036aaea3617SCody P Schafer buf[used] = '/'; 1037aaea3617SCody P Schafer used++; 1038aaea3617SCody P Schafer } 1039aaea3617SCody P Schafer if (sub_non_neg(len, used) > 0) { 1040aaea3617SCody P Schafer buf[used] = '\0'; 1041aaea3617SCody P Schafer used++; 1042aaea3617SCody P Schafer } else 1043aaea3617SCody P Schafer buf[len - 1] = '\0'; 1044aaea3617SCody P Schafer 1045dc098b35SAndi Kleen return buf; 1046dc098b35SAndi Kleen } 1047dc098b35SAndi Kleen 1048dc098b35SAndi Kleen static char *format_alias_or(char *buf, int len, struct perf_pmu *pmu, 1049dc098b35SAndi Kleen struct perf_pmu_alias *alias) 1050dc098b35SAndi Kleen { 1051dc098b35SAndi Kleen snprintf(buf, len, "%s OR %s/%s/", alias->name, pmu->name, alias->name); 1052dc098b35SAndi Kleen return buf; 1053dc098b35SAndi Kleen } 1054dc098b35SAndi Kleen 105508e60ed1SAndi Kleen struct pair { 105608e60ed1SAndi Kleen char *name; 105708e60ed1SAndi Kleen char *desc; 105808e60ed1SAndi Kleen }; 105908e60ed1SAndi Kleen 106008e60ed1SAndi Kleen static int cmp_pair(const void *a, const void *b) 1061dc098b35SAndi Kleen { 106208e60ed1SAndi Kleen const struct pair *as = a; 106308e60ed1SAndi Kleen const struct pair *bs = b; 106408e60ed1SAndi Kleen 106508e60ed1SAndi Kleen /* Put extra events last */ 106608e60ed1SAndi Kleen if (!!as->desc != !!bs->desc) 106708e60ed1SAndi Kleen return !!as->desc - !!bs->desc; 106808e60ed1SAndi Kleen return strcmp(as->name, bs->name); 106908e60ed1SAndi Kleen } 107008e60ed1SAndi Kleen 107108e60ed1SAndi Kleen static void wordwrap(char *s, int start, int max, int corr) 107208e60ed1SAndi Kleen { 107308e60ed1SAndi Kleen int column = start; 107408e60ed1SAndi Kleen int n; 107508e60ed1SAndi Kleen 107608e60ed1SAndi Kleen while (*s) { 107708e60ed1SAndi Kleen int wlen = strcspn(s, " \t"); 107808e60ed1SAndi Kleen 107908e60ed1SAndi Kleen if (column + wlen >= max && column > start) { 108008e60ed1SAndi Kleen printf("\n%*s", start, ""); 108108e60ed1SAndi Kleen column = start + corr; 108208e60ed1SAndi Kleen } 108308e60ed1SAndi Kleen n = printf("%s%.*s", column > start ? " " : "", wlen, s); 108408e60ed1SAndi Kleen if (n <= 0) 108508e60ed1SAndi Kleen break; 108608e60ed1SAndi Kleen s += wlen; 108708e60ed1SAndi Kleen column += n; 108808e60ed1SAndi Kleen while (isspace(*s)) 108908e60ed1SAndi Kleen s++; 109008e60ed1SAndi Kleen } 1091dc098b35SAndi Kleen } 1092dc098b35SAndi Kleen 10931c5f01feSAndi Kleen void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag) 1094dc098b35SAndi Kleen { 1095dc098b35SAndi Kleen struct perf_pmu *pmu; 1096dc098b35SAndi Kleen struct perf_pmu_alias *alias; 1097dc098b35SAndi Kleen char buf[1024]; 1098dc098b35SAndi Kleen int printed = 0; 1099dc098b35SAndi Kleen int len, j; 110008e60ed1SAndi Kleen struct pair *aliases; 110108e60ed1SAndi Kleen int numdesc = 0; 110261eb2eb4SAndi Kleen int columns = pager_get_columns(); 1103dc098b35SAndi Kleen 1104dc098b35SAndi Kleen pmu = NULL; 1105dc098b35SAndi Kleen len = 0; 110642634bc7SAdrian Hunter while ((pmu = perf_pmu__scan(pmu)) != NULL) { 1107dc098b35SAndi Kleen list_for_each_entry(alias, &pmu->aliases, list) 1108dc098b35SAndi Kleen len++; 110942634bc7SAdrian Hunter if (pmu->selectable) 111042634bc7SAdrian Hunter len++; 111142634bc7SAdrian Hunter } 111208e60ed1SAndi Kleen aliases = zalloc(sizeof(struct pair) * len); 1113dc098b35SAndi Kleen if (!aliases) 11147e4772dcSArnaldo Carvalho de Melo goto out_enomem; 1115dc098b35SAndi Kleen pmu = NULL; 1116dc098b35SAndi Kleen j = 0; 111742634bc7SAdrian Hunter while ((pmu = perf_pmu__scan(pmu)) != NULL) { 1118dc098b35SAndi Kleen list_for_each_entry(alias, &pmu->aliases, list) { 111908e60ed1SAndi Kleen char *name = alias->desc ? alias->name : 112008e60ed1SAndi Kleen format_alias(buf, sizeof(buf), pmu, alias); 1121dc098b35SAndi Kleen bool is_cpu = !strcmp(pmu->name, "cpu"); 1122dc098b35SAndi Kleen 1123dc098b35SAndi Kleen if (event_glob != NULL && 1124dc098b35SAndi Kleen !(strglobmatch(name, event_glob) || 1125dc098b35SAndi Kleen (!is_cpu && strglobmatch(alias->name, 1126dc098b35SAndi Kleen event_glob)))) 1127dc098b35SAndi Kleen continue; 11287e4772dcSArnaldo Carvalho de Melo 112908e60ed1SAndi Kleen if (is_cpu && !name_only && !alias->desc) 11307e4772dcSArnaldo Carvalho de Melo name = format_alias_or(buf, sizeof(buf), pmu, alias); 11317e4772dcSArnaldo Carvalho de Melo 113208e60ed1SAndi Kleen aliases[j].name = name; 113308e60ed1SAndi Kleen if (is_cpu && !name_only && !alias->desc) 113408e60ed1SAndi Kleen aliases[j].name = format_alias_or(buf, 113508e60ed1SAndi Kleen sizeof(buf), 113608e60ed1SAndi Kleen pmu, alias); 113708e60ed1SAndi Kleen aliases[j].name = strdup(aliases[j].name); 113808e60ed1SAndi Kleen if (!aliases[j].name) 11397e4772dcSArnaldo Carvalho de Melo goto out_enomem; 114008e60ed1SAndi Kleen 114108e60ed1SAndi Kleen aliases[j].desc = alias->desc; 1142dc098b35SAndi Kleen j++; 1143dc098b35SAndi Kleen } 1144fa52ceabSArnaldo Carvalho de Melo if (pmu->selectable && 1145fa52ceabSArnaldo Carvalho de Melo (event_glob == NULL || strglobmatch(pmu->name, event_glob))) { 11467e4772dcSArnaldo Carvalho de Melo char *s; 11477e4772dcSArnaldo Carvalho de Melo if (asprintf(&s, "%s//", pmu->name) < 0) 11487e4772dcSArnaldo Carvalho de Melo goto out_enomem; 114908e60ed1SAndi Kleen aliases[j].name = s; 115042634bc7SAdrian Hunter j++; 115142634bc7SAdrian Hunter } 115242634bc7SAdrian Hunter } 1153dc098b35SAndi Kleen len = j; 115408e60ed1SAndi Kleen qsort(aliases, len, sizeof(struct pair), cmp_pair); 1155dc098b35SAndi Kleen for (j = 0; j < len; j++) { 1156dc098b35SAndi Kleen if (name_only) { 115708e60ed1SAndi Kleen printf("%s ", aliases[j].name); 1158dc098b35SAndi Kleen continue; 1159dc098b35SAndi Kleen } 11601c5f01feSAndi Kleen if (aliases[j].desc && !quiet_flag) { 116108e60ed1SAndi Kleen if (numdesc++ == 0) 116208e60ed1SAndi Kleen printf("\n"); 116308e60ed1SAndi Kleen printf(" %-50s\n", aliases[j].name); 116408e60ed1SAndi Kleen printf("%*s", 8, "["); 116508e60ed1SAndi Kleen wordwrap(aliases[j].desc, 8, columns, 0); 116608e60ed1SAndi Kleen printf("]\n"); 116708e60ed1SAndi Kleen } else 116808e60ed1SAndi Kleen printf(" %-50s [Kernel PMU event]\n", aliases[j].name); 1169dc098b35SAndi Kleen printed++; 1170dc098b35SAndi Kleen } 1171dfc431cbSArnaldo Carvalho de Melo if (printed && pager_in_use()) 1172dc098b35SAndi Kleen printf("\n"); 11737e4772dcSArnaldo Carvalho de Melo out_free: 11747e4772dcSArnaldo Carvalho de Melo for (j = 0; j < len; j++) 117508e60ed1SAndi Kleen zfree(&aliases[j].name); 11767e4772dcSArnaldo Carvalho de Melo zfree(&aliases); 11777e4772dcSArnaldo Carvalho de Melo return; 11787e4772dcSArnaldo Carvalho de Melo 11797e4772dcSArnaldo Carvalho de Melo out_enomem: 11807e4772dcSArnaldo Carvalho de Melo printf("FATAL: not enough memory to print PMU events\n"); 11817e4772dcSArnaldo Carvalho de Melo if (aliases) 11827e4772dcSArnaldo Carvalho de Melo goto out_free; 1183dc098b35SAndi Kleen } 11844cabc3d1SAndi Kleen 11854cabc3d1SAndi Kleen bool pmu_have_event(const char *pname, const char *name) 11864cabc3d1SAndi Kleen { 11874cabc3d1SAndi Kleen struct perf_pmu *pmu; 11884cabc3d1SAndi Kleen struct perf_pmu_alias *alias; 11894cabc3d1SAndi Kleen 11904cabc3d1SAndi Kleen pmu = NULL; 11914cabc3d1SAndi Kleen while ((pmu = perf_pmu__scan(pmu)) != NULL) { 11924cabc3d1SAndi Kleen if (strcmp(pname, pmu->name)) 11934cabc3d1SAndi Kleen continue; 11944cabc3d1SAndi Kleen list_for_each_entry(alias, &pmu->aliases, list) 11954cabc3d1SAndi Kleen if (!strcmp(alias->name, name)) 11964cabc3d1SAndi Kleen return true; 11974cabc3d1SAndi Kleen } 11984cabc3d1SAndi Kleen return false; 11994cabc3d1SAndi Kleen } 12007d4bdab5SAdrian Hunter 12017d4bdab5SAdrian Hunter static FILE *perf_pmu__open_file(struct perf_pmu *pmu, const char *name) 12027d4bdab5SAdrian Hunter { 12037d4bdab5SAdrian Hunter struct stat st; 12047d4bdab5SAdrian Hunter char path[PATH_MAX]; 12057d4bdab5SAdrian Hunter const char *sysfs; 12067d4bdab5SAdrian Hunter 12077d4bdab5SAdrian Hunter sysfs = sysfs__mountpoint(); 12087d4bdab5SAdrian Hunter if (!sysfs) 12097d4bdab5SAdrian Hunter return NULL; 12107d4bdab5SAdrian Hunter 12117d4bdab5SAdrian Hunter snprintf(path, PATH_MAX, 12127d4bdab5SAdrian Hunter "%s" EVENT_SOURCE_DEVICE_PATH "%s/%s", sysfs, pmu->name, name); 12137d4bdab5SAdrian Hunter 12147d4bdab5SAdrian Hunter if (stat(path, &st) < 0) 12157d4bdab5SAdrian Hunter return NULL; 12167d4bdab5SAdrian Hunter 12177d4bdab5SAdrian Hunter return fopen(path, "r"); 12187d4bdab5SAdrian Hunter } 12197d4bdab5SAdrian Hunter 12207d4bdab5SAdrian Hunter int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt, 12217d4bdab5SAdrian Hunter ...) 12227d4bdab5SAdrian Hunter { 12237d4bdab5SAdrian Hunter va_list args; 12247d4bdab5SAdrian Hunter FILE *file; 12257d4bdab5SAdrian Hunter int ret = EOF; 12267d4bdab5SAdrian Hunter 12277d4bdab5SAdrian Hunter va_start(args, fmt); 12287d4bdab5SAdrian Hunter file = perf_pmu__open_file(pmu, name); 12297d4bdab5SAdrian Hunter if (file) { 12307d4bdab5SAdrian Hunter ret = vfscanf(file, fmt, args); 12317d4bdab5SAdrian Hunter fclose(file); 12327d4bdab5SAdrian Hunter } 12337d4bdab5SAdrian Hunter va_end(args); 12347d4bdab5SAdrian Hunter return ret; 12357d4bdab5SAdrian Hunter } 1236