| /****************************************************************************** |
| * |
| * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| * |
| * NAME |
| * librttest.h |
| * |
| * 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 -- Josh Triplett |
| * 2007-Jul-26: Renamed to librttest.h -- Josh Triplett |
| * 2009-Nov-4: TSC macros within another header -- Giuseppe Cavallaro |
| * |
| *****************************************************************************/ |
| |
| #ifndef LIBRTTEST_H |
| #define LIBRTTEST_H |
| |
| #include <sys/syscall.h> |
| #include <errno.h> |
| #include <getopt.h> |
| #include <math.h> |
| #include <pthread.h> |
| #include <sched.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <signal.h> |
| #include <string.h> |
| #include <time.h> |
| #include <unistd.h> |
| #include "list.h" |
| #include "realtime_config.h" |
| |
| extern void setup(); |
| extern void cleanup(); |
| |
| extern int optind, opterr, optopt; |
| extern char *optarg; |
| |
| #define _MAXTHREADS 256 |
| #define CALIBRATE_LOOPS 100000 |
| unsigned long iters_per_us; |
| |
| #define NS_PER_MS 1000000 |
| #define NS_PER_US 1000 |
| #define NS_PER_SEC 1000000000 |
| #define US_PER_MS 1000 |
| #define US_PER_SEC 1000000 |
| #define MS_PER_SEC 1000 |
| |
| typedef unsigned long long nsec_t; |
| |
| struct thread { |
| struct list_head _threads; |
| pthread_t pthread; |
| pthread_attr_t attr; |
| pthread_mutex_t mutex; |
| pthread_cond_t cond; |
| void *arg; |
| void *(*func)(void*); |
| int priority; |
| int policy; |
| int flags; |
| int id; |
| }; |
| typedef struct { volatile int counter; } atomic_t; |
| |
| // flags for threads |
| #define THREAD_START 1 |
| #define THREAD_QUIT 2 |
| #define thread_quit(T) (((T)->flags) & THREAD_QUIT) |
| |
| #define PRINT_BUFFER_SIZE (1024*1024*4) |
| #define ULL_MAX 18446744073709551615ULL // (1 << 64) - 1 |
| |
| extern pthread_mutex_t _buffer_mutex; |
| extern char * _print_buffer; |
| extern int _print_buffer_offset; |
| extern int _dbg_lvl; |
| extern double pass_criteria; |
| |
| /* function prototypes */ |
| |
| /* atomic_add - add integer to atomic variable and returns a value. |
| * i: integer value to add |
| * v: pointer of type atomic_t |
| */ |
| static inline int atomic_add(int i, atomic_t *v) |
| { |
| /* XXX (garrcoop): only available in later versions of gcc */ |
| #if HAVE___SYNC_ADD_AND_FETCH |
| return __sync_add_and_fetch(&v->counter, i); |
| #else |
| printf("%s: %s\n", __func__, strerror(ENOSYS)); |
| exit(1); |
| return -1; |
| #endif |
| } |
| /* atomic_inc: atomically increment the integer passed by reference |
| */ |
| static inline int atomic_inc(atomic_t *v) |
| { |
| return atomic_add(1, v); |
| } |
| |
| /* atomic_get: atomically get the integer passed by reference |
| */ |
| //#define atomic_get(v) ((v)->counter) |
| static inline int atomic_get(atomic_t *v) |
| { |
| return v->counter; |
| } |
| |
| /* atomic_set: atomically get the integer passed by reference |
| */ |
| //#define atomic_set(i,v) ((v)->counter = (i)) |
| static inline void atomic_set(int i, atomic_t *v) |
| { |
| v->counter = i; |
| } |
| |
| /* buffer_init: initialize the buffered printing system |
| */ |
| void buffer_init(); |
| |
| /* buffer_print: prints the contents of the buffer |
| */ |
| void buffer_print(); |
| |
| /* buffer_fini: destroy the buffer |
| */ |
| void buffer_fini(); |
| |
| /* debug: do debug prints at level L (see DBG_* below). If buffer_init |
| * has been called previously, this will print to the internal memory |
| * buffer rather than to stderr. |
| * L: debug level (see below) This will print if L is lower than _dbg_lvl |
| * A: format string (printf style) |
| * B: args to format string (printf style) |
| */ |
| static volatile int _debug_count = 0; |
| #define debug(L,A,B...) do {\ |
| if ((L) <= _dbg_lvl) {\ |
| pthread_mutex_lock(&_buffer_mutex);\ |
| if (_print_buffer) {\ |
| if (PRINT_BUFFER_SIZE - _print_buffer_offset < 1000)\ |
| buffer_print();\ |
| _print_buffer_offset += snprintf(&_print_buffer[_print_buffer_offset],\ |
| PRINT_BUFFER_SIZE - _print_buffer_offset, "%06d: "A, _debug_count++, ##B);\ |
| } else {\ |
| fprintf(stderr, "%06d: "A, _debug_count++, ##B);\ |
| }\ |
| pthread_mutex_unlock(&_buffer_mutex);\ |
| }\ |
| } while (0) |
| #define DBG_ERR 1 |
| #define DBG_WARN 2 |
| #define DBG_INFO 3 |
| #define DBG_DEBUG 4 |
| |
| /* rt_help: print help for standard args */ |
| void rt_help(); |
| |
| /* rt_init_long: initialize librttest |
| * options: pass in an getopt style string of options -- e.g. "ab:cd::e:" |
| * if this or parse_arg is null, no option parsing will be done |
| * on behalf of the calling program (only internal args will be parsed) |
| * longopts: a pointer to the first element of an array of struct option, the |
| * last entry must be set to all zeros. |
| * parse_arg: a function that will get called when one of the above |
| * options is encountered on the command line. It will be passed |
| * the option -- e.g. 'b' -- and the value. Something like: |
| * // assume we passed "af:z::" to rt_init |
| * int parse_my_options(int option, char *value) { |
| * int handled = 1; |
| * switch (option) { |
| * case 'a': |
| * alphanum = 1; |
| * break; |
| * case 'f': |
| * // we passed f: which means f has an argument |
| * freedom = strcpy(value); |
| * break; |
| * case 'z': |
| * // we passed z:: which means z has an optional argument |
| * if (value) |
| * zero_size = atoi(value); |
| * else |
| * zero_size++; |
| * default: |
| * handled = 0; |
| * } |
| * return handled; |
| * } |
| * argc: passed from main |
| * argv: passed from main |
| */ |
| int rt_init_long(const char *options, const struct option *longopts, |
| int (*parse_arg)(int option, char *value), |
| int argc, char *argv[]); |
| |
| /* rt_init: same as rt_init_long with no long options */ |
| int rt_init(const char *options, int (*parse_arg)(int option, char *value), |
| int argc, char *argv[]); |
| |
| int create_thread(void*(*func)(void*), void *arg, int prio, int policy); |
| |
| /* create_fifo_thread: spawn a SCHED_FIFO thread with priority prio running |
| * func as the thread function with arg as it's parameter. |
| * func: |
| * arg: argument to func |
| * prio: 1-100, 100 being highest priority |
| */ |
| int create_fifo_thread(void*(*func)(void*), void *arg, int prio); |
| |
| /* create_rr_thread: spawn a SCHED_RR thread with priority prio running |
| * func as the thread function with arg as it's parameter. |
| * func: |
| * arg: argument to func |
| * prio: 1-100, 100 being highest priority |
| */ |
| int create_rr_thread(void*(*func)(void*), void *arg, int prio); |
| |
| /* create_other_thread: spawn a SCHED_OTHER thread |
| * func as the thread function with arg as it's parameter. |
| * func: |
| * arg: argument to func |
| */ |
| int create_other_thread(void*(*func)(void*), void *arg); |
| |
| /* Change the priority of a running thread */ |
| int set_thread_priority(pthread_t pthread, int prio); |
| |
| /* Change the priority of the current context (usually called from main()) |
| * and its policy to SCHED_FIFO |
| * prio: 1-99 |
| */ |
| int set_priority(int prio); |
| |
| /* all_threads_quit: signal all threads to quit */ |
| void all_threads_quit(void); |
| |
| /* join_threads: wait for all threads to finish |
| * (calls all_threads_quit interally) |
| */ |
| void join_threads(); |
| |
| /* get_thread: return a struct thread pointer from the list */ |
| struct thread * get_thread(int i); |
| |
| /* signal thread i to quit and then call join */ |
| void join_thread(int i); |
| |
| /* return the delta in ts_delta |
| * ts_end > ts_start |
| * if ts_delta is not null, the difference will be returned in it |
| */ |
| void ts_minus(struct timespec *ts_end, struct timespec *ts_start, struct timespec *ts_delta); |
| |
| /* return the sum in ts_sum |
| * all arguments are not null |
| */ |
| void ts_plus(struct timespec *ts_a, struct timespec *ts_b, struct timespec *ts_sum); |
| |
| /* put a ts into proper form (nsec < NS_PER_SEC) |
| * ts must not be null |
| */ |
| void ts_normalize(struct timespec *ts); |
| |
| /* convert nanoseconds to a timespec |
| * ts must not be null |
| */ |
| void nsec_to_ts(nsec_t ns, struct timespec *ts); |
| |
| /* convert a timespec to nanoseconds |
| * ts must not be null |
| */ |
| int ts_to_nsec(struct timespec *ts, nsec_t *ns); |
| |
| /* return difference in microseconds */ |
| unsigned long long tsc_minus(unsigned long long tsc_start, unsigned long long tsc_end); |
| |
| /* rt_nanosleep: sleep for ns nanoseconds using clock_nanosleep |
| */ |
| void rt_nanosleep(nsec_t ns); |
| |
| /* rt_nanosleep: sleep until absolute time ns given in |
| * nanoseconds using clock_nanosleep |
| */ |
| void rt_nanosleep_until(nsec_t ns); |
| |
| /* rt_gettime: get CLOCK_MONOTONIC time in nanoseconds |
| */ |
| nsec_t rt_gettime(); |
| |
| /* busy_work_ms: do busy work for ms milliseconds |
| */ |
| void *busy_work_ms(int ms); |
| |
| /* busy_work_us: do busy work for us microseconds |
| */ |
| void *busy_work_us(int us); |
| |
| /* init_pi_mutex: initialize a pthread mutex to have PI support |
| */ |
| void init_pi_mutex(pthread_mutex_t *m); |
| |
| /* latency_trace_enable: Enable latency tracing via sysctls. |
| */ |
| void latency_trace_enable(void); |
| |
| /* latency_trace_start: Start tracing latency; call before running test. |
| */ |
| void latency_trace_start(void); |
| |
| /* latency_trace_stop: Stop tracing latency; call immediately after observing |
| * excessive latency and stopping test. |
| */ |
| void latency_trace_stop(void); |
| |
| /* latency_trace_print: Print latency trace information from |
| * /proc/latency_trace. |
| */ |
| void latency_trace_print(void); |
| |
| #endif /* LIBRTTEST_H */ |