1 /******************************************************************************
2 *
3 * Copyright © International Business Machines Corp., 2006-2008
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
13 * the GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 * NAME
20 * librttest.h
21 *
22 * DESCRIPTION
23 * A set of commonly used convenience functions for writing
24 * threaded realtime test cases.
25 *
26 * USAGE:
27 * To be included in testcases.
28 *
29 * AUTHOR
30 * Darren Hart <dvhltc@us.ibm.com>
31 *
32 * HISTORY
33 * 2006-Apr-26: Initial version by Darren Hart
34 * 2006-May-08: Added atomic_{inc,set,get}, thread struct, debug function,
35 * rt_init, buffered printing -- Vernon Mauery
36 * 2006-May-09: improved command line argument handling
37 * 2007-Jul-12: Added latency tracing functions -- Josh Triplett
38 * 2007-Jul-26: Renamed to librttest.h -- Josh Triplett
39 * 2009-Nov-4: TSC macros within another header -- Giuseppe Cavallaro
40 *
41 *****************************************************************************/
42
43 #ifndef LIBRTTEST_H
44 #define LIBRTTEST_H
45
46 #include <sys/syscall.h>
47 #include <errno.h>
48 #include <getopt.h>
49 #include <math.h>
50 #include <pthread.h>
51 #include <sched.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <signal.h>
55 #include <string.h>
56 #include <time.h>
57 #include <unistd.h>
58 #include "list.h"
59 #include "realtime_config.h"
60
61 extern void setup(void);
62 extern void cleanup(int i);
63
64 extern int optind, opterr, optopt;
65 extern char *optarg;
66
67 #define _MAXTHREADS 256
68 #define CALIBRATE_LOOPS 100000
69
70 #define NS_PER_MS 1000000
71 #define NS_PER_US 1000
72 #define NS_PER_SEC 1000000000
73 #define US_PER_MS 1000
74 #define US_PER_SEC 1000000
75 #define MS_PER_SEC 1000
76
77 typedef unsigned long long nsec_t;
78
79 struct thread {
80 struct list_head _threads;
81 pthread_t pthread;
82 pthread_attr_t attr;
83 pthread_mutex_t mutex;
84 pthread_cond_t cond;
85 void *arg;
86 void *(*func)(void*);
87 int priority;
88 int policy;
89 int flags;
90 int id;
91 };
92 typedef struct { volatile int counter; } atomic_t;
93
94 // flags for threads
95 #define THREAD_START 1
96 #define THREAD_QUIT 2
97 #define thread_quit(T) (((T)->flags) & THREAD_QUIT)
98
99 #define PRINT_BUFFER_SIZE (1024*1024*4)
100 #define ULL_MAX 18446744073709551615ULL // (1 << 64) - 1
101
102 extern pthread_mutex_t _buffer_mutex;
103 extern char * _print_buffer;
104 extern int _print_buffer_offset;
105 extern int _dbg_lvl;
106 extern double pass_criteria;
107
108 /* function prototypes */
109
110 /* atomic_add - add integer to atomic variable and returns a value.
111 * i: integer value to add
112 * v: pointer of type atomic_t
113 */
atomic_add(int i,atomic_t * v)114 static inline int atomic_add(int i, atomic_t *v)
115 {
116 /* XXX (garrcoop): only available in later versions of gcc */
117 #if HAVE___SYNC_ADD_AND_FETCH
118 return __sync_add_and_fetch(&v->counter, i);
119 #else
120 printf("%s: %s\n", __func__, strerror(ENOSYS));
121 exit(1);
122 return -1;
123 #endif
124 }
125 /* atomic_inc: atomically increment the integer passed by reference
126 */
atomic_inc(atomic_t * v)127 static inline int atomic_inc(atomic_t *v)
128 {
129 return atomic_add(1, v);
130 }
131
132 /* atomic_get: atomically get the integer passed by reference
133 */
134 //#define atomic_get(v) ((v)->counter)
atomic_get(atomic_t * v)135 static inline int atomic_get(atomic_t *v)
136 {
137 return v->counter;
138 }
139
140 /* atomic_set: atomically get the integer passed by reference
141 */
142 //#define atomic_set(i,v) ((v)->counter = (i))
atomic_set(int i,atomic_t * v)143 static inline void atomic_set(int i, atomic_t *v)
144 {
145 v->counter = i;
146 }
147
148 /* buffer_init: initialize the buffered printing system
149 */
150 void buffer_init(void);
151
152 /* buffer_print: prints the contents of the buffer
153 */
154 void buffer_print(void);
155
156 /* buffer_fini: destroy the buffer
157 */
158 void buffer_fini(void);
159
160 /* debug: do debug prints at level L (see DBG_* below). If buffer_init
161 * has been called previously, this will print to the internal memory
162 * buffer rather than to stderr.
163 * L: debug level (see below) This will print if L is lower than _dbg_lvl
164 * A: format string (printf style)
165 * B: args to format string (printf style)
166 */
167 static volatile int _debug_count = 0;
168 #define debug(L,A,B...) do {\
169 if ((L) <= _dbg_lvl) {\
170 pthread_mutex_lock(&_buffer_mutex);\
171 if (_print_buffer) {\
172 if (PRINT_BUFFER_SIZE - _print_buffer_offset < 1000)\
173 buffer_print();\
174 _print_buffer_offset += snprintf(&_print_buffer[_print_buffer_offset],\
175 PRINT_BUFFER_SIZE - _print_buffer_offset, "%06d: "A, _debug_count++, ##B);\
176 } else {\
177 fprintf(stderr, "%06d: "A, _debug_count++, ##B);\
178 }\
179 pthread_mutex_unlock(&_buffer_mutex);\
180 }\
181 } while (0)
182 #define DBG_ERR 1
183 #define DBG_WARN 2
184 #define DBG_INFO 3
185 #define DBG_DEBUG 4
186
187 /* rt_help: print help for standard args */
188 void rt_help(void);
189
190 /* rt_init_long: initialize librttest
191 * options: pass in an getopt style string of options -- e.g. "ab:cd::e:"
192 * if this or parse_arg is null, no option parsing will be done
193 * on behalf of the calling program (only internal args will be parsed)
194 * longopts: a pointer to the first element of an array of struct option, the
195 * last entry must be set to all zeros.
196 * parse_arg: a function that will get called when one of the above
197 * options is encountered on the command line. It will be passed
198 * the option -- e.g. 'b' -- and the value. Something like:
199 * // assume we passed "af:z::" to rt_init
200 * int parse_my_options(int option, char *value) {
201 * int handled = 1;
202 * switch (option) {
203 * case 'a':
204 * alphanum = 1;
205 * break;
206 * case 'f':
207 * // we passed f: which means f has an argument
208 * freedom = strcpy(value);
209 * break;
210 * case 'z':
211 * // we passed z:: which means z has an optional argument
212 * if (value)
213 * zero_size = atoi(value);
214 * else
215 * zero_size++;
216 * default:
217 * handled = 0;
218 * }
219 * return handled;
220 * }
221 * argc: passed from main
222 * argv: passed from main
223 */
224 int rt_init_long(const char *options, const struct option *longopts,
225 int (*parse_arg)(int option, char *value),
226 int argc, char *argv[]);
227
228 /* rt_init: same as rt_init_long with no long options */
229 int rt_init(const char *options, int (*parse_arg)(int option, char *value),
230 int argc, char *argv[]);
231
232 int create_thread(void*(*func)(void*), void *arg, int prio, int policy);
233
234 /* create_fifo_thread: spawn a SCHED_FIFO thread with priority prio running
235 * func as the thread function with arg as it's parameter.
236 * func:
237 * arg: argument to func
238 * prio: 1-100, 100 being highest priority
239 */
240 int create_fifo_thread(void*(*func)(void*), void *arg, int prio);
241
242 /* create_rr_thread: spawn a SCHED_RR thread with priority prio running
243 * func as the thread function with arg as it's parameter.
244 * func:
245 * arg: argument to func
246 * prio: 1-100, 100 being highest priority
247 */
248 int create_rr_thread(void*(*func)(void*), void *arg, int prio);
249
250 /* create_other_thread: spawn a SCHED_OTHER thread
251 * func as the thread function with arg as it's parameter.
252 * func:
253 * arg: argument to func
254 */
255 int create_other_thread(void*(*func)(void*), void *arg);
256
257 /* Change the priority of a running thread */
258 int set_thread_priority(pthread_t pthread, int prio);
259
260 /* Change the priority of the current context (usually called from main())
261 * and its policy to SCHED_FIFO
262 * prio: 1-99
263 */
264 int set_priority(int prio);
265
266 /* all_threads_quit: signal all threads to quit */
267 void all_threads_quit(void);
268
269 /* join_threads: wait for all threads to finish
270 * (calls all_threads_quit interally)
271 */
272 void join_threads(void);
273
274 /* get_thread: return a struct thread pointer from the list */
275 struct thread * get_thread(int i);
276
277 /* signal thread i to quit and then call join */
278 void join_thread(int i);
279
280 /* return the delta in ts_delta
281 * ts_end > ts_start
282 * if ts_delta is not null, the difference will be returned in it
283 */
284 void ts_minus(struct timespec *ts_end, struct timespec *ts_start, struct timespec *ts_delta);
285
286 /* return the sum in ts_sum
287 * all arguments are not null
288 */
289 void ts_plus(struct timespec *ts_a, struct timespec *ts_b, struct timespec *ts_sum);
290
291 /* put a ts into proper form (nsec < NS_PER_SEC)
292 * ts must not be null
293 */
294 void ts_normalize(struct timespec *ts);
295
296 /* convert nanoseconds to a timespec
297 * ts must not be null
298 */
299 void nsec_to_ts(nsec_t ns, struct timespec *ts);
300
301 /* convert a timespec to nanoseconds
302 * ts must not be null
303 */
304 int ts_to_nsec(struct timespec *ts, nsec_t *ns);
305
306 /* return difference in microseconds */
307 unsigned long long tsc_minus(unsigned long long tsc_start, unsigned long long tsc_end);
308
309 /* rt_nanosleep: sleep for ns nanoseconds using clock_nanosleep
310 */
311 void rt_nanosleep(nsec_t ns);
312
313 /* rt_nanosleep: sleep until absolute time ns given in
314 * nanoseconds using clock_nanosleep
315 */
316 void rt_nanosleep_until(nsec_t ns);
317
318 /* rt_gettime: get CLOCK_MONOTONIC time in nanoseconds
319 */
320 nsec_t rt_gettime(void);
321
322 /* busy_work_ms: do busy work for ms milliseconds
323 */
324 void *busy_work_ms(int ms);
325
326 /* busy_work_us: do busy work for us microseconds
327 */
328 void *busy_work_us(int us);
329
330 /* init_pi_mutex: initialize a pthread mutex to have PI support
331 */
332 void init_pi_mutex(pthread_mutex_t *m);
333
334 /* latency_trace_enable: Enable latency tracing via sysctls.
335 */
336 void latency_trace_enable(void);
337
338 /* latency_trace_start: Start tracing latency; call before running test.
339 */
340 void latency_trace_start(void);
341
342 /* latency_trace_stop: Stop tracing latency; call immediately after observing
343 * excessive latency and stopping test.
344 */
345 void latency_trace_stop(void);
346
347 /* latency_trace_print: Print latency trace information from
348 * /proc/latency_trace.
349 */
350 void latency_trace_print(void);
351
352 #endif /* LIBRTTEST_H */
353