| /* |
| * Copyright (C) 2009 Carsten Emde <carsten.emde@osadl.org> |
| * Copyright (C) 2010 Clark Williams <williams@redhat.com> |
| * |
| * based on functions from cyclictest that has |
| * (C) 2008-2009 Clark Williams <williams@redhat.com> |
| * (C) 2005-2007 Thomas Gleixner <tglx@linutronix.de> |
| */ |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sched.h> |
| #include <stdarg.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <unistd.h> |
| #include <sys/syscall.h> /* For SYS_gettid definitions */ |
| #include "rt-utils.h" |
| #include "rt-sched.h" |
| #include "error.h" |
| |
| static char debugfileprefix[MAX_PATH]; |
| |
| /* |
| * Finds the tracing directory in a mounted debugfs |
| */ |
| char *get_debugfileprefix(void) |
| { |
| char type[100]; |
| FILE *fp; |
| int size; |
| int found = 0; |
| struct stat s; |
| |
| if (debugfileprefix[0] != '\0') |
| goto out; |
| |
| /* look in the "standard" mount point first */ |
| if ((stat("/sys/kernel/debug/tracing", &s) == 0) && S_ISDIR(s.st_mode)) { |
| strcpy(debugfileprefix, "/sys/kernel/debug/tracing/"); |
| goto out; |
| } |
| |
| /* now look in the "other standard" place */ |
| if ((stat("/debug/tracing", &s) == 0) && S_ISDIR(s.st_mode)) { |
| strcpy(debugfileprefix, "/debug/tracing/"); |
| goto out; |
| } |
| |
| /* oh well, parse /proc/mounts and see if it's there */ |
| if ((fp = fopen("/proc/mounts","r")) == NULL) |
| goto out; |
| |
| while (fscanf(fp, "%*s %" |
| STR(MAX_PATH) |
| "s %99s %*s %*d %*d\n", |
| debugfileprefix, type) == 2) { |
| if (strcmp(type, "debugfs") == 0) { |
| found = 1; |
| break; |
| } |
| /* stupid check for systemd-style autofs mount */ |
| if ((strcmp(debugfileprefix, "/sys/kernel/debug") == 0) && |
| (strcmp(type, "systemd") == 0)) { |
| found = 1; |
| break; |
| } |
| } |
| fclose(fp); |
| |
| if (!found) { |
| debugfileprefix[0] = '\0'; |
| goto out; |
| } |
| |
| size = sizeof(debugfileprefix) - strlen(debugfileprefix); |
| strncat(debugfileprefix, "/tracing/", size); |
| |
| out: |
| return debugfileprefix; |
| } |
| |
| int mount_debugfs(char *path) |
| { |
| char *mountpoint = path; |
| char cmd[MAX_PATH]; |
| char *prefix; |
| int ret; |
| |
| /* if it's already mounted just return */ |
| prefix = get_debugfileprefix(); |
| if (strlen(prefix) != 0) { |
| info("debugfs mountpoint: %s\n", prefix); |
| return 0; |
| } |
| if (!mountpoint) |
| mountpoint = "/sys/kernel/debug"; |
| |
| sprintf(cmd, "mount -t debugfs debugfs %s", mountpoint); |
| ret = system(cmd); |
| if (ret != 0) { |
| fprintf(stderr, "Error mounting debugfs at %s: %s\n", mountpoint, strerror(errno)); |
| return -1; |
| } |
| return 0; |
| |
| } |
| |
| static char **tracer_list; |
| static char *tracer_buffer; |
| static int num_tracers; |
| #define CHUNKSZ 1024 |
| |
| /* |
| * return a list of the tracers configured into the running kernel |
| */ |
| |
| int get_tracers(char ***list) |
| { |
| int ret; |
| FILE *fp; |
| char buffer[CHUNKSZ]; |
| char *prefix = get_debugfileprefix(); |
| char *tmpbuf = NULL; |
| char *ptr; |
| int tmpsz = 0; |
| |
| /* if we've already parse it, return what we have */ |
| if (tracer_list) { |
| *list = tracer_list; |
| return num_tracers; |
| } |
| |
| /* open the tracing file available_tracers */ |
| sprintf(buffer, "%savailable_tracers", prefix); |
| if ((fp = fopen(buffer, "r")) == NULL) |
| fatal ("Can't open %s for reading\n", buffer); |
| |
| /* allocate initial buffer */ |
| ptr = tmpbuf = malloc(CHUNKSZ); |
| if (ptr == NULL) |
| fatal("error allocating initial space for tracer list\n"); |
| |
| /* read in the list of available tracers */ |
| while((ret = fread(buffer, sizeof(char), CHUNKSZ, fp))) { |
| if ((ptr+ret+1) > (tmpbuf+tmpsz)) { |
| tmpbuf = realloc(tmpbuf, tmpsz + CHUNKSZ); |
| if (tmpbuf == NULL) |
| fatal("error allocating space for list of valid tracers\n"); |
| tmpsz += CHUNKSZ; |
| } |
| strncpy(ptr, buffer, ret); |
| ptr += ret; |
| } |
| fclose(fp); |
| if (tmpsz == 0) |
| fatal("error reading available tracers\n"); |
| |
| tracer_buffer = tmpbuf; |
| |
| /* get a buffer for the pointers to tracers */ |
| if (!(tracer_list = malloc(sizeof(char *)))) |
| fatal ("error allocatinging tracer list buffer\n"); |
| |
| /* parse the buffer */ |
| ptr = strtok(tmpbuf, " \t\n\r"); |
| do { |
| tracer_list[num_tracers++] = ptr; |
| tracer_list = realloc(tracer_list, sizeof(char*)*(num_tracers+1)); |
| tracer_list[num_tracers] = NULL; |
| } while ((ptr = strtok(NULL, " \t\n\r")) != NULL); |
| |
| /* return the list and number of tracers */ |
| *list = tracer_list; |
| return num_tracers; |
| } |
| |
| |
| /* |
| * return zero if tracername is not a valid tracer, non-zero if it is |
| */ |
| |
| int valid_tracer(char *tracername) |
| { |
| char **list; |
| int ntracers; |
| int i; |
| |
| ntracers = get_tracers(&list); |
| if (ntracers == 0 || tracername == NULL) |
| return 0; |
| for (i = 0; i < ntracers; i++) |
| if (strncmp(list[i], tracername, strlen(list[i])) == 0) |
| return 1; |
| return 0; |
| } |
| |
| /* |
| * enable event tracepoint |
| */ |
| int setevent(char *event, char *val) |
| { |
| char *prefix = get_debugfileprefix(); |
| char buffer[MAX_PATH]; |
| int fd; |
| int ret; |
| |
| sprintf(buffer, "%s%s", prefix, event); |
| if ((fd = open(buffer, O_WRONLY)) < 0) { |
| warn("unable to open %s\n", buffer); |
| return -1; |
| } |
| if ((ret = write(fd, val, strlen(val))) < 0) { |
| warn("unable to write %s to %s\n", val, buffer); |
| close(fd); |
| return -1; |
| } |
| close(fd); |
| return 0; |
| } |
| |
| int event_enable_all(void) |
| { |
| return setevent("events/enable", "1"); |
| } |
| |
| int event_disable_all(void) |
| { |
| return setevent("events/enable", "0"); |
| } |
| |
| int event_enable(char *event) |
| { |
| char path[MAX_PATH]; |
| sprintf(path, "events/%s/enable", event); |
| return setevent(path, "1"); |
| } |
| |
| int event_disable(char *event) |
| { |
| char path[MAX_PATH]; |
| sprintf(path, "events/%s/enable", event); |
| return setevent(path, "0"); |
| } |
| |
| int check_privs(void) |
| { |
| int policy = sched_getscheduler(0); |
| struct sched_param param, old_param; |
| |
| /* if we're already running a realtime scheduler |
| * then we *should* be able to change things later |
| */ |
| if (policy == SCHED_FIFO || policy == SCHED_RR) |
| return 0; |
| |
| /* first get the current parameters */ |
| if (sched_getparam(0, &old_param)) { |
| fprintf(stderr, "unable to get scheduler parameters\n"); |
| return 1; |
| } |
| param = old_param; |
| |
| /* try to change to SCHED_FIFO */ |
| param.sched_priority = 1; |
| if (sched_setscheduler(0, SCHED_FIFO, ¶m)) { |
| fprintf(stderr, "Unable to change scheduling policy!\n"); |
| fprintf(stderr, "either run as root or join realtime group\n"); |
| return 1; |
| } |
| |
| /* we're good; change back and return success */ |
| return sched_setscheduler(0, policy, &old_param); |
| } |
| |
| const char *policy_to_string(int policy) |
| { |
| switch (policy) { |
| case SCHED_OTHER: |
| return "SCHED_OTHER"; |
| case SCHED_FIFO: |
| return "SCHED_FIFO"; |
| case SCHED_RR: |
| return "SCHED_RR"; |
| case SCHED_BATCH: |
| return "SCHED_BATCH"; |
| case SCHED_IDLE: |
| return "SCHED_IDLE"; |
| case SCHED_DEADLINE: |
| return "SCHED_DEADLINE"; |
| } |
| |
| return "unknown"; |
| } |
| |
| uint32_t string_to_policy(const char *str) |
| { |
| if (!strcmp(str, "other")) |
| return SCHED_OTHER; |
| else if (!strcmp(str, "fifo")) |
| return SCHED_FIFO; |
| else if (!strcmp(str, "rr")) |
| return SCHED_RR; |
| else if (!strcmp(str, "batch")) |
| return SCHED_BATCH; |
| else if (!strcmp(str, "idle")) |
| return SCHED_IDLE; |
| else if (!strcmp(str, "deadline")) |
| return SCHED_DEADLINE; |
| |
| return 0; |
| } |
| |
| pid_t gettid(void) |
| { |
| return syscall(SYS_gettid); |
| } |