| #include "cpumap.h" |
| #include "evsel.h" |
| #include "evlist.h" |
| #include "ras.h" |
| #include "debugfs.h" |
| |
| #include <regex.h> |
| |
| #define EVENT_STR_MAX_LENGTH 1024 |
| #define SYS_NAME_SEPARATOR ":" |
| #define RASD_CFG_FILE "rasd.cfg" |
| |
| unsigned int page_size; |
| struct perf_evlist *evlist; |
| struct perf_rasd rasd; |
| |
| /* Check if the read line is a valid config file line.*/ |
| static bool cfg_valid_line(char *str) |
| { |
| regex_t reg; |
| int ret; |
| |
| ret = regcomp(®, "^[:space:]*[a-z]+:[a-z_]+[:space:]*$", |
| REG_EXTENDED | |
| REG_NEWLINE); /* for matching $ */ |
| if (ret) |
| err("compiling cfg regex"); |
| |
| ret = regexec(®, str, 0, NULL, 0); |
| if (ret) { |
| if (ret != REG_NOMATCH) |
| printf("error matching %s\n", str); |
| return false; |
| } |
| return true; |
| } |
| |
| /* |
| * Extract the event subsystem and event names from the command |
| * line argument. The syntax is 'sys:name'. |
| */ |
| static int extract_sys_name(char **str, char **sys, char **name) |
| { |
| /* Look for the sys:name separator */ |
| char *sys_ptr = strsep(str, SYS_NAME_SEPARATOR); |
| |
| if (!*str || !sys_ptr) |
| return -1; |
| |
| /* Is there something after the separator? */ |
| if (!strlen(*str)) |
| return -1; |
| |
| /* Alloc and fill the sys part */ |
| *sys = strndup(sys_ptr, EVENT_STR_MAX_LENGTH); |
| |
| /* |
| * Look for the newline in the rest of the string. |
| * If found alloc and fill the name part. |
| */ |
| *name = strndup(strsep(str, "\n\r"), EVENT_STR_MAX_LENGTH); |
| |
| return 0; |
| } |
| |
| /* Add the requested tracepoint event to evlist */ |
| static void add_tp_event(char *event_str) |
| { |
| struct perf_evsel *tp; |
| char **str = &event_str; |
| static int idx; |
| |
| if (extract_sys_name(str, &rasd.sys, &rasd.name)) |
| err("invalid event specified, syntax is sys:name"); |
| |
| /* Initialize tracepoint evsel */ |
| tp = perf_evsel__newtp_idx(rasd.sys, rasd.name, idx); |
| if (!tp) |
| err("init tracepoint evsel, idx: %d", idx); |
| |
| /* Add the event to the lists of events, enable events ids */ |
| perf_evlist__add(evlist, tp); |
| perf_evsel__set_sample_id(tp, false); |
| perf_evsel__calc_id_pos(tp); |
| |
| idx++; |
| } |
| |
| /* |
| * Read the config file for events to enable |
| */ |
| static int read_config_file(void) |
| { |
| FILE *file; |
| char *event_str; |
| int ret = 0; |
| |
| file = fopen(RASD_CFG_FILE, "r"); |
| if (!file) |
| return -ENODEV; |
| |
| event_str = malloc(EVENT_STR_MAX_LENGTH); |
| if (!event_str) |
| return -ENOMEM; |
| |
| /* Read config file, line by line */ |
| while (fgets(event_str, EVENT_STR_MAX_LENGTH, file)) |
| if (cfg_valid_line(event_str)) |
| add_tp_event(event_str); |
| |
| if (ferror(file)) |
| ret = -EINVAL; |
| |
| free(event_str); |
| fclose(file); |
| |
| return ret; |
| } |
| |
| int main() |
| { |
| struct perf_evsel *c; |
| struct thread_map *threads; |
| struct cpu_map *cpus; |
| |
| page_size = sysconf(_SC_PAGE_SIZE); |
| |
| if (!debugfs_mount(NULL)) |
| err("mounting debugfs"); |
| |
| evlist = perf_evlist__new(); |
| if (!evlist) |
| err("allocating evlist"); |
| |
| if (read_config_file()) |
| err("error reading config file"); |
| |
| threads = thread_map__new(-1, getpid(), UINT_MAX); |
| if (!threads) |
| err("allocating threads_map\n"); |
| |
| cpus = cpu_map__new(NULL); |
| if (!cpus) |
| err("allocating cpu_map\n"); |
| |
| perf_evlist__set_maps(evlist, cpus, threads); |
| |
| /* On all online CPUs by default, system wide tracing */ |
| evlist__for_each(evlist, c) { |
| if (perf_evsel__open(c, evlist->cpus, NULL) < 0) |
| err("opening tracepoint, are you root?"); |
| } |
| perf_evlist__set_id_pos(evlist); |
| |
| /* mmap buffers */ |
| if (perf_evlist__mmap(evlist, 4 /* opts->mmap_pages */, false) < 0) |
| err("Failed to mmap with %d (%s)\n", errno, strerror(errno)); |
| |
| /* Enable at kernel level */ |
| perf_evlist__enable(evlist); |
| |
| perf_evlist__delete(evlist); |
| |
| free(rasd.name); |
| free(rasd.sys); |
| |
| return 0; |
| } |