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