/*
|
* 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);
|
}
|