Home | History | Annotate | Download | only in pthread_kill_latency
      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 (at) in.ibm.com>
     38  *
     39  * HISTORY
     40  *     	2006-Jun-28:  Initial version by Sripathi Kodi <sripathik (at) in.ibm.com>
     41  *	2007-Nov-07:  Added libstats support by Darren Hart <dvhltc (at) us.ibm.com>
     42  *	2008-Jan-23:  Latency tracing added by
     43  *				Sebastien Dugue <sebastien.dugue (at) 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 
     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 
     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 
    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 
    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 
    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