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 * pthread_kill_latency.c
21 *
22 * DESCRIPTION
23 * Measure the latency involved in sending a signal to a thread
24 * using pthread_kill. Two threads are created - the one that receives the
25 * signal (thread1) and the other that sends the signal (thread2). Before
26 * sending the signal, the thread2 waits for thread1 to initialize, notes
27 * the time and sends pthread_kill signal to thread1. thread2, which has
28 * defined a handler for the signal, notes the time it receives the signal.
29 * The maximum and the minimum latency is reported.
30 *
31 *
32 * USAGE:
33 * Use run_auto.sh script in current directory to build and run test.
34 * pthread_kill_latency [-v{1234}]
35 *
36 * AUTHOR
37 * Sripathi Kodi <sripathik@in.ibm.com>
38 *
39 * HISTORY
40 * 2006-Jun-28: Initial version by Sripathi Kodi <sripathik@in.ibm.com>
41 * 2007-Nov-07: Added libstats support by Darren Hart <dvhltc@us.ibm.com>
42 * 2008-Jan-23: Latency tracing added by
43 * Sebastien Dugue <sebastien.dugue@bull.net>
44 *
45 * This line has to be added to avoid a stupid CVS problem
46 *****************************************************************************/
47
48 #include <stdio.h>
49 #include <time.h>
50 #include <pthread.h>
51 #include <sched.h>
52 #include <signal.h>
53 #include <errno.h>
54 #include <librttest.h>
55 #include <libstats.h>
56
57 #define PRIO 89
58 #define ITERATIONS 10000
59 #define HIST_BUCKETS 100
60 #define THRESHOLD 20
61 #define SIGNALNUMBER SIGUSR1
62
63 /* Get the pthread structure corresponding to this thread id */
64 #define PTHREADOF(tid) get_thread(tid)->pthread
65
66 static long latency_threshold = 0;
67 nsec_t begin, end;
68 int fail;
69
70 atomic_t flag;
71
usage(void)72 void usage(void)
73 {
74 rt_help();
75 printf("pthread_kill_latency specific options:\n");
76 printf(" -l threshold trace latency with given threshold in us\n");
77 }
78
parse_args(int c,char * v)79 int parse_args(int c, char *v)
80 {
81
82 int handled = 1;
83 switch (c) {
84 case 'l':
85 latency_threshold = strtoull(v, NULL, 0);
86 break;
87 case 'h':
88 usage();
89 exit(0);
90 default:
91 handled = 0;
92 break;
93 }
94 return handled;
95 }
96
97 #if 0
98 /* Set up a signal handler */
99 int rt_setsighandler(int signum, void (*handler) (int))
100 {
101 struct sigaction sa;
102 memset(&sa, 0, sizeof(sa));
103 sa.sa_handler = handler;
104 if (sigaction(signum, &sa, NULL) != 0) {
105 perror("Sigaction failed:\n");
106 return 1;
107 }
108 return 0;
109 }
110 #endif
111
signal_receiving_thread(void * arg)112 void *signal_receiving_thread(void *arg)
113 {
114 int i, ret, sig;
115 long delta;
116 long max, min;
117 sigset_t set, oset;
118
119 stats_container_t dat;
120 stats_container_t hist;
121 stats_quantiles_t quantiles;
122 stats_record_t rec;
123
124 stats_container_init(&dat, ITERATIONS);
125 stats_container_init(&hist, HIST_BUCKETS);
126 stats_quantiles_init(&quantiles, (int)log10(ITERATIONS));
127
128 debug(DBG_DEBUG, "Signal receiving thread running\n");
129
130 if ((sigaddset(&set, SIGNALNUMBER))) {
131 perror("sigaddset:");
132 exit(1);
133 }
134 if ((ret = pthread_sigmask(SIG_BLOCK, &set, &oset))) {
135 printf("pthread_sigmask returned %d\n", ret);
136 exit(1);
137 }
138
139 /* Let the sending thread know that receiver is ready */
140 atomic_set(1, &flag);
141
142 debug(DBG_DEBUG, "Signal receiving thread ready to receive\n");
143
144 if (latency_threshold) {
145 latency_trace_enable();
146 latency_trace_start();
147 }
148
149 /* Warm up */
150 for (i = 0; i < 5; i++) {
151 sigwait(&set, &sig);
152 atomic_set(1, &flag);
153 }
154
155 max = min = 0;
156 fail = 0;
157 debug(DBG_INFO, "\n\n");
158
159 for (i = 0; i < ITERATIONS; i++) {
160 sigwait(&set, &sig);
161 end = rt_gettime();
162 delta = (end - begin) / NS_PER_US;
163 rec.x = i;
164 rec.y = delta;
165 stats_container_append(&dat, rec);
166
167 if (i == 0 || delta < min)
168 min = delta;
169
170 if (delta > max)
171 max = delta;
172
173 if (delta > pass_criteria)
174 fail++;
175
176 debug(DBG_INFO, "Iteration %d: Took %ld us. Max = %ld us, "
177 "Min = %ld us\n", i, delta, max, min);
178
179 fflush(stdout);
180 buffer_print();
181
182 if (latency_threshold && (delta > latency_threshold)) {
183 atomic_set(2, &flag);
184 break;
185 }
186
187 atomic_set(1, &flag);
188 }
189
190 if (latency_threshold) {
191 latency_trace_stop();
192
193 if (i != ITERATIONS) {
194 printf
195 ("Latency threshold (%luus) exceeded at iteration %d\n",
196 latency_threshold, i);
197 fflush(stdout);
198 buffer_print();
199 latency_trace_print();
200 stats_container_resize(&dat, i + 1);
201 }
202 }
203
204 stats_hist(&hist, &dat);
205 stats_container_save("samples", "pthread_kill Latency Scatter Plot",
206 "Iteration", "Latency (us)", &dat, "points");
207 stats_container_save("hist", "pthread_kill Latency Histogram",
208 "Latency (us)", "Samples", &hist, "steps");
209
210 printf("\n");
211 printf("Min: %lu us\n", stats_min(&dat));
212 printf("Max: %lu us\n", stats_max(&dat));
213 printf("Avg: %.4f us\n", stats_avg(&dat));
214 printf("StdDev: %.4f us\n", stats_stddev(&dat));
215 printf("Quantiles:\n");
216 stats_quantiles_calc(&dat, &quantiles);
217 stats_quantiles_print(&quantiles);
218 printf("Failures: %d\n", fail);
219 printf("Criteria: Time < %d us\n", (int)pass_criteria);
220 printf("Result: %s", fail ? "FAIL" : "PASS");
221 printf("\n\n");
222
223 return NULL;
224 }
225
signal_sending_thread(void * arg)226 void *signal_sending_thread(void *arg)
227 {
228 int target_thread = (intptr_t) ((struct thread *)arg)->arg;
229 int i, ret;
230
231 debug(DBG_INFO, "Signal sending thread: target thread id =%d\n",
232 (int)PTHREADOF(target_thread));
233
234 /* Wait for the receiving thread to initialize */
235 while (!atomic_get(&flag)) {
236 usleep(100);
237 };
238 atomic_set(0, &flag);
239
240 /* Warm up */
241 for (i = 0; i < 5; i++) {
242
243 debug(DBG_DEBUG, "Sending signal (Warm up). Loopcnt = %d\n", i);
244
245 if ((ret =
246 pthread_kill(PTHREADOF(target_thread), SIGNALNUMBER))) {
247 printf("pthread_kill returned %d\n", ret);
248 }
249 /* Wait till the receiving thread processes the signal */
250 while (!atomic_get(&flag)) {
251 usleep(100);
252 };
253 atomic_set(0, &flag);
254 }
255 for (i = 0; i < ITERATIONS; i++) {
256
257 debug(DBG_DEBUG, "Sending signal. Loopcnt = %d\n", i);
258
259 /* Record the time just before sending the signal */
260 begin = rt_gettime();
261 if ((ret =
262 pthread_kill(PTHREADOF(target_thread), SIGNALNUMBER))) {
263 printf("pthread_kill returned %d\n", ret);
264 }
265 /* Wait till the receiving thread processes the signal */
266 while (!atomic_get(&flag)) {
267 usleep(100);
268 }
269
270 if (atomic_get(&flag) == 2)
271 break;
272
273 atomic_set(0, &flag);
274 }
275 return NULL;
276 }
277
main(int argc,char * argv[])278 int main(int argc, char *argv[])
279 {
280 int thr_id1, thr_id2;
281
282 atomic_set(0, &flag);
283 setup();
284
285 pass_criteria = THRESHOLD;
286 rt_init("l:h", parse_args, argc, argv); /* we need the buffered print system */
287
288 printf("-------------------------------\n");
289 printf("pthread_kill Latency\n");
290 printf("-------------------------------\n\n");
291
292 printf("Iterations: %d\n", ITERATIONS);
293
294 debug(DBG_DEBUG, "Main creating threads\n");
295 fflush(stdout);
296
297 thr_id1 = create_fifo_thread(signal_receiving_thread, NULL, PRIO);
298 thr_id2 =
299 create_fifo_thread(signal_sending_thread,
300 (void *)(intptr_t) thr_id1, PRIO - 1);
301 // thr_id2 = create_other_thread(signal_sending_thread, (void*)(intptr_t)thr_id1);
302
303 debug(DBG_DEBUG, "Main joining threads debug\n");
304 join_thread(thr_id1);
305 join_thread(thr_id2);
306 buffer_print();
307
308 return fail;
309 }
310