You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
729 lines
17 KiB
729 lines
17 KiB
/****************************************************************************** |
|
* |
|
* Copyright © International Business Machines Corp., 2006-2008 |
|
* |
|
* This program is free software; you can redistribute it and/or modify |
|
* it under the terms of the GNU General Public License as published by |
|
* the Free Software Foundation; either version 2 of the License, or |
|
* (at your option) any later version. |
|
* |
|
* This program is distributed in the hope that it will be useful, |
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See |
|
* the GNU General Public License for more details. |
|
* |
|
* You should have received a copy of the GNU General Public License |
|
* along with this program; if not, write to the Free Software |
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
|
* |
|
* NAME |
|
* librttest.c |
|
* |
|
* DESCRIPTION |
|
* A set of commonly used convenience functions for writing |
|
* threaded realtime test cases. |
|
* |
|
* USAGE: |
|
* To be included in testcases. |
|
* |
|
* AUTHOR |
|
* Darren Hart <dvhltc@us.ibm.com> |
|
* |
|
* HISTORY |
|
* 2006-Apr-26: Initial version by Darren Hart |
|
* 2006-May-08: Added atomic_{inc,set,get}, thread struct, debug function, |
|
* rt_init, buffered printing -- Vernon Mauery |
|
* 2006-May-09: improved command line argument handling |
|
* 2007-Jul-12: Added latency tracing functions and I/O helper functions |
|
* -- Josh triplett |
|
* 2008-Jan-10: Added RR thread support to tests -- Chirag Jog |
|
* |
|
*****************************************************************************/ |
|
|
|
#include <librttest.h> |
|
#include <libstats.h> |
|
|
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#include <signal.h> |
|
#include <time.h> |
|
#include <string.h> |
|
#include <pthread.h> |
|
#include <sched.h> |
|
#include <errno.h> |
|
#include <unistd.h> |
|
#include <getopt.h> |
|
#include <sys/prctl.h> |
|
#include <sys/stat.h> |
|
#include <sys/syscall.h> |
|
#include <sys/types.h> |
|
#include <sys/mman.h> |
|
#include <fcntl.h> |
|
#include <math.h> |
|
|
|
static LIST_HEAD(_threads); |
|
static atomic_t _thread_count = { -1 }; |
|
|
|
pthread_mutex_t _buffer_mutex; |
|
char *_print_buffer = NULL; |
|
int _print_buffer_offset = 0; |
|
int _dbg_lvl = 0; |
|
double pass_criteria; |
|
|
|
static int _use_pi = 1; |
|
|
|
/* function implementations */ |
|
void rt_help(void) |
|
{ |
|
printf("librt standard options:\n"); |
|
printf |
|
(" -b(0,1) 1:enable buffered output, 0:diable buffered output\n"); |
|
printf(" -p(0,1) 0:don't use pi mutexes, 1:use pi mutexes\n"); |
|
printf(" -m use mlockall\n"); |
|
printf |
|
(" -v[0-4] 0:no debug, 1:DBG_ERR, 2:DBG_WARN, 3:DBG_INFO, 4:DBG_DEBUG\n"); |
|
printf(" -s Enable saving stats data (default disabled)\n"); |
|
printf(" -c Set pass criteria\n"); |
|
} |
|
|
|
/* Calibrate the busy work loop */ |
|
void calibrate_busyloop(void) |
|
{ |
|
volatile int i = CALIBRATE_LOOPS; |
|
nsec_t start, end; |
|
|
|
start = rt_gettime(); |
|
while (--i > 0) { |
|
continue; |
|
} |
|
end = rt_gettime(); |
|
|
|
iters_per_us = (CALIBRATE_LOOPS * NS_PER_US) / (end - start); |
|
} |
|
|
|
int rt_init_long(const char *options, const struct option *longopts, |
|
int (*parse_arg) (int option, char *value), int argc, |
|
char *argv[]) |
|
{ |
|
const struct option *cur_opt; |
|
int use_buffer = 1; |
|
char *longopt_vals; |
|
size_t i; |
|
int c; |
|
opterr = 0; |
|
int mlock = 0; |
|
char *all_options; |
|
|
|
if (asprintf(&all_options, ":b:mp:v:sc:%s", options) == -1) { |
|
fprintf(stderr, |
|
"Failed to allocate string for option string\n"); |
|
exit(1); |
|
} |
|
|
|
/* Check for duplicate options in optstring */ |
|
for (i = 0; i < strlen(all_options); i++) { |
|
char opt = all_options[i]; |
|
|
|
if (opt == ':') |
|
continue; |
|
|
|
/* Search ahead */ |
|
if (strchr(&all_options[i + 1], opt)) { |
|
fprintf(stderr, |
|
"Programmer error -- argument -%c already used at least twice\n", |
|
opt); |
|
exit(1); |
|
} |
|
} |
|
|
|
/* Ensure each long options has a known unique short option in val. */ |
|
longopt_vals = ""; |
|
cur_opt = longopts; |
|
while (cur_opt && cur_opt->name) { |
|
if (cur_opt->flag) { |
|
fprintf(stderr, "Programmer error -- argument --%s flag" |
|
" is non-null\n", cur_opt->name); |
|
exit(1); |
|
} |
|
if (!strchr(all_options, cur_opt->val)) { |
|
fprintf(stderr, "Programmer error -- argument --%s " |
|
"shortopt -%c wasn't listed in options (%s)\n", |
|
cur_opt->name, cur_opt->val, all_options); |
|
exit(1); |
|
} |
|
if (strchr(longopt_vals, cur_opt->val)) { |
|
fprintf(stderr, "Programmer error -- argument --%s " |
|
"shortopt -%c is used more than once\n", |
|
cur_opt->name, cur_opt->val); |
|
exit(1); |
|
} |
|
if (asprintf(&longopt_vals, "%s%c", longopt_vals, cur_opt->val) |
|
< 0) { |
|
perror("asprintf"); |
|
exit(2); |
|
} |
|
cur_opt++; |
|
} |
|
|
|
while ((c = getopt_long(argc, argv, all_options, longopts, NULL)) != -1) { |
|
switch (c) { |
|
case 'c': |
|
pass_criteria = atof(optarg); |
|
break; |
|
case 'b': |
|
use_buffer = atoi(optarg); |
|
break; |
|
case 'p': |
|
_use_pi = atoi(optarg); |
|
break; |
|
case 'm': |
|
mlock = 1; |
|
break; |
|
case 'v': |
|
_dbg_lvl = atoi(optarg); |
|
break; |
|
case 's': |
|
save_stats = 1; |
|
break; |
|
case ':': |
|
if (optopt == '-') |
|
fprintf(stderr, "long option missing arg\n"); |
|
else |
|
fprintf(stderr, "option -%c: missing arg\n", |
|
optopt); |
|
parse_arg('h', optarg); /* Just to display usage */ |
|
exit(1); /* Just in case. (should normally be done by usage()) */ |
|
case '?': |
|
if (optopt == '-') |
|
fprintf(stderr, "unrecognized long option\n"); |
|
else |
|
fprintf(stderr, "option -%c not recognized\n", |
|
optopt); |
|
parse_arg('h', optarg); /* Just to display usage */ |
|
exit(1); /* Just in case. (should normally be done by usage()) */ |
|
default: |
|
if (parse_arg && parse_arg(c, optarg)) |
|
break; /* Application option */ |
|
|
|
fprintf(stderr, |
|
"Programmer error -- option -%c defined but not handled\n", |
|
c); |
|
exit(1); |
|
} |
|
} |
|
if (!_use_pi) |
|
printf |
|
("Priority Inheritance has been disabled for this run.\n"); |
|
if (use_buffer) |
|
buffer_init(); |
|
if (mlock) { |
|
if (mlockall(MCL_CURRENT | MCL_FUTURE)) { |
|
perror("failed to lock memory\n"); |
|
exit(1); |
|
} |
|
} |
|
|
|
calibrate_busyloop(); |
|
|
|
/* |
|
* atexit() order matters here - buffer_print() will be called before |
|
* buffer_fini(). |
|
*/ |
|
atexit(buffer_fini); |
|
atexit(buffer_print); |
|
return 0; |
|
} |
|
|
|
int rt_init(const char *options, int (*parse_arg) (int option, char *value), |
|
int argc, char *argv[]) |
|
{ |
|
return rt_init_long(options, NULL, parse_arg, argc, argv); |
|
} |
|
|
|
void buffer_init(void) |
|
{ |
|
_print_buffer = malloc(PRINT_BUFFER_SIZE); |
|
if (!_print_buffer) |
|
fprintf(stderr, |
|
"insufficient memory for print buffer - printing directly to stderr\n"); |
|
else |
|
memset(_print_buffer, 0, PRINT_BUFFER_SIZE); |
|
} |
|
|
|
void buffer_print(void) |
|
{ |
|
if (_print_buffer) { |
|
fprintf(stderr, "%s", _print_buffer); |
|
memset(_print_buffer, 0, PRINT_BUFFER_SIZE); |
|
_print_buffer_offset = 0; |
|
} |
|
} |
|
|
|
void buffer_fini(void) |
|
{ |
|
if (_print_buffer) |
|
free(_print_buffer); |
|
_print_buffer = NULL; |
|
} |
|
|
|
void cleanup(int i) |
|
{ |
|
printf("Test terminated with asynchronous signal\n"); |
|
buffer_print(); |
|
buffer_fini(); |
|
if (i) |
|
exit(i); |
|
} |
|
|
|
void setup() |
|
{ |
|
signal(SIGINT, cleanup); |
|
signal(SIGQUIT, cleanup); |
|
signal(SIGTERM, cleanup); |
|
} |
|
|
|
int create_thread(void *(*func) (void *), void *arg, int prio, int policy) |
|
{ |
|
struct sched_param param; |
|
int id, ret; |
|
struct thread *thread; |
|
|
|
id = atomic_inc(&_thread_count); |
|
|
|
thread = malloc(sizeof(struct thread)); |
|
if (!thread) |
|
return -1; |
|
|
|
list_add_tail(&thread->_threads, &_threads); |
|
pthread_cond_init(&thread->cond, NULL); // Accept the defaults |
|
init_pi_mutex(&thread->mutex); |
|
thread->id = id; |
|
thread->priority = prio; |
|
thread->policy = policy; |
|
thread->flags = 0; |
|
thread->arg = arg; |
|
thread->func = func; |
|
param.sched_priority = prio; |
|
|
|
pthread_attr_init(&thread->attr); |
|
pthread_attr_setinheritsched(&thread->attr, PTHREAD_EXPLICIT_SCHED); |
|
pthread_attr_setschedpolicy(&thread->attr, thread->policy); |
|
pthread_attr_setschedparam(&thread->attr, ¶m); |
|
|
|
if ((ret = |
|
pthread_create(&thread->pthread, &thread->attr, func, |
|
(void *)thread))) { |
|
printf("pthread_create failed: %d (%s)\n", ret, strerror(ret)); |
|
list_del(&thread->_threads); |
|
pthread_attr_destroy(&thread->attr); |
|
free(thread); |
|
return -1; |
|
} |
|
pthread_attr_destroy(&thread->attr); |
|
|
|
return id; |
|
} |
|
|
|
int create_fifo_thread(void *(*func) (void *), void *arg, int prio) |
|
{ |
|
return create_thread(func, arg, prio, SCHED_FIFO); |
|
} |
|
|
|
int create_rr_thread(void *(*func) (void *), void *arg, int prio) |
|
{ |
|
return create_thread(func, arg, prio, SCHED_RR); |
|
} |
|
|
|
int create_other_thread(void *(*func) (void *), void *arg) |
|
{ |
|
return create_thread(func, arg, 0, SCHED_OTHER); |
|
} |
|
|
|
int set_thread_priority(pthread_t pthread, int prio) |
|
{ |
|
struct sched_param sched_param; |
|
sched_param.sched_priority = prio; |
|
int policy; |
|
|
|
policy = (prio > 0) ? SCHED_FIFO : SCHED_OTHER; |
|
|
|
return pthread_setschedparam(pthread, policy, &sched_param); |
|
} |
|
|
|
int set_priority(int prio) |
|
{ |
|
struct sched_param sp; |
|
int ret = 0; |
|
|
|
sp.sched_priority = prio; |
|
if (sched_setscheduler(0, SCHED_FIFO, &sp) != 0) { |
|
perror("sched_setscheduler"); |
|
ret = -1; |
|
} |
|
return ret; |
|
} |
|
|
|
void join_thread(int i) |
|
{ |
|
struct thread *p, *t = NULL; |
|
list_for_each_entry(p, &_threads, _threads) { |
|
if (p->id == i) { |
|
t = p; |
|
break; |
|
} |
|
} |
|
if (t) { |
|
t->flags |= THREAD_QUIT; |
|
if (t->pthread) |
|
pthread_join(t->pthread, NULL); |
|
list_del(&t->_threads); |
|
} |
|
} |
|
|
|
void all_threads_quit(void) |
|
{ |
|
struct thread *p; |
|
list_for_each_entry(p, &_threads, _threads) { |
|
p->flags |= THREAD_QUIT; |
|
} |
|
} |
|
|
|
void join_threads(void) |
|
{ |
|
all_threads_quit(); |
|
struct thread *p, *t; |
|
list_for_each_entry_safe(p, t, &_threads, _threads) { |
|
if (p->pthread) |
|
pthread_join(p->pthread, NULL); |
|
list_del(&p->_threads); |
|
} |
|
} |
|
|
|
struct thread *get_thread(int i) |
|
{ |
|
struct thread *p; |
|
list_for_each_entry(p, &_threads, _threads) { |
|
if (p->id == i) { |
|
return p; |
|
} |
|
} |
|
return NULL; |
|
} |
|
|
|
void ts_minus(struct timespec *ts_end, struct timespec *ts_start, |
|
struct timespec *ts_delta) |
|
{ |
|
if (ts_end == NULL || ts_start == NULL || ts_delta == NULL) { |
|
printf("ERROR in %s: one or more of the timespecs is NULL", |
|
__FUNCTION__); |
|
return; |
|
} |
|
|
|
ts_delta->tv_sec = ts_end->tv_sec - ts_start->tv_sec; |
|
ts_delta->tv_nsec = ts_end->tv_nsec - ts_start->tv_nsec; |
|
ts_normalize(ts_delta); |
|
} |
|
|
|
void ts_plus(struct timespec *ts_a, struct timespec *ts_b, |
|
struct timespec *ts_sum) |
|
{ |
|
if (ts_a == NULL || ts_b == NULL || ts_sum == NULL) { |
|
printf("ERROR in %s: one or more of the timespecs is NULL", |
|
__FUNCTION__); |
|
return; |
|
} |
|
|
|
ts_sum->tv_sec = ts_a->tv_sec + ts_b->tv_sec; |
|
ts_sum->tv_nsec = ts_a->tv_nsec + ts_b->tv_nsec; |
|
ts_normalize(ts_sum); |
|
} |
|
|
|
void ts_normalize(struct timespec *ts) |
|
{ |
|
if (ts == NULL) { |
|
/* FIXME: write a real error logging system */ |
|
printf("ERROR in %s: ts is NULL\n", __FUNCTION__); |
|
return; |
|
} |
|
|
|
/* get the abs(nsec) < NS_PER_SEC */ |
|
while (ts->tv_nsec > NS_PER_SEC) { |
|
ts->tv_sec++; |
|
ts->tv_nsec -= NS_PER_SEC; |
|
} |
|
while (ts->tv_nsec < -NS_PER_SEC) { |
|
ts->tv_sec--; |
|
ts->tv_nsec += NS_PER_SEC; |
|
} |
|
|
|
/* get the values to the same polarity */ |
|
if (ts->tv_sec > 0 && ts->tv_nsec < 0) { |
|
ts->tv_sec--; |
|
ts->tv_nsec += NS_PER_SEC; |
|
} |
|
if (ts->tv_sec < 0 && ts->tv_nsec > 0) { |
|
ts->tv_sec++; |
|
ts->tv_nsec -= NS_PER_SEC; |
|
} |
|
} |
|
|
|
int ts_to_nsec(struct timespec *ts, nsec_t * ns) |
|
{ |
|
struct timespec t; |
|
if (ts == NULL) { |
|
/* FIXME: write a real error logging system */ |
|
printf("ERROR in %s: ts is NULL\n", __FUNCTION__); |
|
return -1; |
|
} |
|
t.tv_sec = ts->tv_sec; |
|
t.tv_nsec = ts->tv_nsec; |
|
ts_normalize(&t); |
|
|
|
if (t.tv_sec <= 0 && t.tv_nsec < 0) { |
|
printf("ERROR in %s: ts is negative\n", __FUNCTION__); |
|
return -1; |
|
} |
|
|
|
*ns = (nsec_t) ts->tv_sec * NS_PER_SEC + ts->tv_nsec; |
|
return 0; |
|
} |
|
|
|
void nsec_to_ts(nsec_t ns, struct timespec *ts) |
|
{ |
|
if (ts == NULL) { |
|
/* FIXME: write a real error logging system */ |
|
printf("ERROR in %s: ts is NULL\n", __FUNCTION__); |
|
return; |
|
} |
|
ts->tv_sec = ns / NS_PER_SEC; |
|
ts->tv_nsec = ns % NS_PER_SEC; |
|
} |
|
|
|
/* return difference in microseconds */ |
|
unsigned long long tsc_minus(unsigned long long tsc_start, |
|
unsigned long long tsc_end) |
|
{ |
|
unsigned long long delta; |
|
if (tsc_start <= tsc_end) |
|
delta = tsc_end - tsc_start; |
|
else { |
|
delta = ULL_MAX - (tsc_end - tsc_start) + 1; |
|
printf("TSC wrapped, delta=%llu\n", delta); |
|
} |
|
return delta; |
|
} |
|
|
|
void rt_nanosleep_until(nsec_t ns) |
|
{ |
|
struct timespec ts_sleep, ts_rem; |
|
int rc; |
|
nsec_to_ts(ns, &ts_sleep); |
|
rc = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts_sleep, |
|
&ts_rem); |
|
/* FIXME: when should we display the remainder ? */ |
|
if (rc != 0) { |
|
printf("WARNING: rt_nanosleep() returned early by %d s %d ns\n", |
|
(int)ts_rem.tv_sec, (int)ts_rem.tv_nsec); |
|
} |
|
} |
|
|
|
void rt_nanosleep(nsec_t ns) |
|
{ |
|
struct timespec ts_sleep, ts_rem; |
|
int rc; |
|
nsec_to_ts(ns, &ts_sleep); |
|
rc = clock_nanosleep(CLOCK_MONOTONIC, 0, &ts_sleep, &ts_rem); |
|
/* FIXME: when should we display the remainder ? */ |
|
if (rc != 0) { |
|
printf("WARNING: rt_nanosleep() returned early by %d s %d ns\n", |
|
(int)ts_rem.tv_sec, (int)ts_rem.tv_nsec); |
|
} |
|
} |
|
|
|
nsec_t rt_gettime(void) |
|
{ |
|
struct timespec ts; |
|
nsec_t ns; |
|
int rc; |
|
|
|
rc = clock_gettime(CLOCK_MONOTONIC, &ts); |
|
if (rc != 0) { |
|
printf("ERROR in %s: clock_gettime() returned %d\n", |
|
__FUNCTION__, rc); |
|
perror("clock_gettime() failed"); |
|
return 0; |
|
} |
|
|
|
ts_to_nsec(&ts, &ns); |
|
return ns; |
|
} |
|
|
|
void *busy_work_ms(int ms) |
|
{ |
|
busy_work_us(ms * US_PER_MS); |
|
return NULL; |
|
} |
|
|
|
void *busy_work_us(int us) |
|
{ |
|
volatile int i; |
|
nsec_t start, now; |
|
int delta; /* time in us */ |
|
|
|
i = us * iters_per_us; |
|
|
|
start = rt_gettime(); |
|
while (--i > 0) { |
|
continue; |
|
} |
|
now = rt_gettime(); |
|
|
|
delta = (now - start) / NS_PER_US; |
|
/* uncomment to tune to your machine */ |
|
/* printf("busy_work_us requested: %dus actual: %dus\n", us, delta); */ |
|
return NULL; |
|
} |
|
|
|
void init_pi_mutex(pthread_mutex_t * m) |
|
{ |
|
#if HAS_PRIORITY_INHERIT |
|
pthread_mutexattr_t attr; |
|
int ret; |
|
int protocol; |
|
|
|
if ((ret = pthread_mutexattr_init(&attr)) != 0) { |
|
printf("Failed to init mutexattr: %d (%s)\n", ret, |
|
strerror(ret)); |
|
}; |
|
if (_use_pi |
|
&& (ret = |
|
pthread_mutexattr_setprotocol(&attr, |
|
PTHREAD_PRIO_INHERIT)) != 0) { |
|
printf("Can't set protocol prio inherit: %d (%s)\n", ret, |
|
strerror(ret)); |
|
} |
|
if ((ret = pthread_mutexattr_getprotocol(&attr, &protocol)) != 0) { |
|
printf("Can't get mutexattr protocol: %d (%s)\n", ret, |
|
strerror(ret)); |
|
} |
|
if ((ret = pthread_mutex_init(m, &attr)) != 0) { |
|
printf("Failed to init mutex: %d (%s)\n", ret, strerror(ret)); |
|
} |
|
#endif |
|
|
|
/* FIXME: does any of this need to be destroyed ? */ |
|
} |
|
|
|
/* Write the entirety of data. Complain if unable to do so. */ |
|
static void write_or_complain(int fd, const void *data, size_t len) |
|
{ |
|
const char *remaining = data; |
|
|
|
while (len > 0) { |
|
ssize_t ret = write(fd, remaining, len); |
|
if (ret <= 0) { |
|
if (errno != EAGAIN && errno != EINTR) { |
|
perror("write"); |
|
return; |
|
} |
|
} else { |
|
remaining += ret; |
|
len -= ret; |
|
} |
|
} |
|
} |
|
|
|
/* Write the given data to the existing file specified by pathname. Complain |
|
* if unable to do so. */ |
|
static void write_file(const char *pathname, const void *data, size_t len) |
|
{ |
|
int fd = open(pathname, O_WRONLY); |
|
if (fd < 0) { |
|
printf("Failed to open file \"%s\": %d (%s)\n", |
|
pathname, errno, strerror(errno)); |
|
return; |
|
} |
|
|
|
write_or_complain(fd, data, len); |
|
|
|
if (close(fd) < 0) { |
|
printf("Failed to close file \"%s\": %d (%s)\n", |
|
pathname, errno, strerror(errno)); |
|
} |
|
} |
|
|
|
/* Write the given '\0'-terminated string to the existing file specified by |
|
* pathname. Complain if unable to do so. */ |
|
static void write_string_to_file(const char *pathname, const char *string) |
|
{ |
|
write_file(pathname, string, strlen(string)); |
|
} |
|
|
|
static void read_and_print(const char *pathname, int output_fd) |
|
{ |
|
char data[4096]; |
|
int fd = open(pathname, O_RDONLY); |
|
if (fd < 0) { |
|
printf("Failed to open file \"%s\": %d (%s)\n", |
|
pathname, errno, strerror(errno)); |
|
return; |
|
} |
|
|
|
while (1) { |
|
ssize_t ret = read(fd, data, sizeof(data)); |
|
if (ret < 0) { |
|
if (errno != EAGAIN && errno != EINTR) { |
|
printf |
|
("Failed to read from file \"%s\": %d (%s)\n", |
|
pathname, errno, strerror(errno)); |
|
break; |
|
} |
|
} else if (ret == 0) |
|
break; |
|
else |
|
write_or_complain(output_fd, data, ret); |
|
} |
|
|
|
if (close(fd) < 0) { |
|
printf("Failed to close file \"%s\": %d (%s)\n", |
|
pathname, errno, strerror(errno)); |
|
} |
|
} |
|
|
|
void latency_trace_enable(void) |
|
{ |
|
printf("Enabling latency tracer.\n"); |
|
write_string_to_file("/proc/sys/kernel/trace_use_raw_cycles", "1"); |
|
write_string_to_file("/proc/sys/kernel/trace_all_cpus", "1"); |
|
write_string_to_file("/proc/sys/kernel/trace_enabled", "1"); |
|
write_string_to_file("/proc/sys/kernel/trace_freerunning", "1"); |
|
write_string_to_file("/proc/sys/kernel/trace_print_on_crash", "0"); |
|
write_string_to_file("/proc/sys/kernel/trace_user_triggered", "1"); |
|
write_string_to_file("/proc/sys/kernel/trace_user_trigger_irq", "-1"); |
|
write_string_to_file("/proc/sys/kernel/trace_verbose", "0"); |
|
write_string_to_file("/proc/sys/kernel/preempt_thresh", "0"); |
|
write_string_to_file("/proc/sys/kernel/wakeup_timing", "0"); |
|
write_string_to_file("/proc/sys/kernel/mcount_enabled", "1"); |
|
write_string_to_file("/proc/sys/kernel/preempt_max_latency", "0"); |
|
} |
|
|
|
#ifndef PR_SET_TRACING |
|
#define PR_SET_TRACING 0 |
|
#endif |
|
|
|
void latency_trace_start(void) |
|
{ |
|
if (prctl(PR_SET_TRACING, 1) < 0) |
|
perror("Failed to start tracing"); |
|
} |
|
|
|
void latency_trace_stop(void) |
|
{ |
|
if (prctl(PR_SET_TRACING, 0) < 0) |
|
perror("Failed to stop tracing"); |
|
} |
|
|
|
void latency_trace_print(void) |
|
{ |
|
read_and_print("/proc/latency_trace", STDOUT_FILENO); |
|
}
|
|
|