• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 recieves 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