1 /* 2 * 3 * sched-messaging.c 4 * 5 * messaging: Benchmark for scheduler and IPC mechanisms 6 * 7 * Based on hackbench by Rusty Russell <rusty (at) rustcorp.com.au> 8 * Ported to perf by Hitoshi Mitake <mitake (at) dcl.info.waseda.ac.jp> 9 * 10 */ 11 12 #include "../perf.h" 13 #include "../util/util.h" 14 #include "../util/parse-options.h" 15 #include "../builtin.h" 16 #include "bench.h" 17 18 /* Test groups of 20 processes spraying to 20 receivers */ 19 /* ANDROID_CHANGE_BEGIN */ 20 #ifdef __BIONIC__ 21 #include <asm/page.h> 22 #endif 23 /* ANDROID_CHANGE_END */ 24 #include <pthread.h> 25 #include <stdio.h> 26 #include <stdlib.h> 27 #include <string.h> 28 #include <errno.h> 29 #include <unistd.h> 30 #include <sys/types.h> 31 #include <sys/socket.h> 32 #include <sys/wait.h> 33 #include <sys/time.h> 34 #include <sys/poll.h> 35 #include <limits.h> 36 37 #define DATASIZE 100 38 39 static bool use_pipes = false; 40 static unsigned int loops = 100; 41 static bool thread_mode = false; 42 static unsigned int num_groups = 10; 43 44 struct sender_context { 45 unsigned int num_fds; 46 int ready_out; 47 int wakefd; 48 int out_fds[0]; 49 }; 50 51 struct receiver_context { 52 unsigned int num_packets; 53 int in_fds[2]; 54 int ready_out; 55 int wakefd; 56 }; 57 58 static void barf(const char *msg) 59 { 60 fprintf(stderr, "%s (error: %s)\n", msg, strerror(errno)); 61 exit(1); 62 } 63 64 static void fdpair(int fds[2]) 65 { 66 if (use_pipes) { 67 if (pipe(fds) == 0) 68 return; 69 } else { 70 if (socketpair(AF_UNIX, SOCK_STREAM, 0, fds) == 0) 71 return; 72 } 73 74 barf(use_pipes ? "pipe()" : "socketpair()"); 75 } 76 77 /* Block until we're ready to go */ 78 static void ready(int ready_out, int wakefd) 79 { 80 char dummy; 81 struct pollfd pollfd = { .fd = wakefd, .events = POLLIN }; 82 83 /* Tell them we're ready. */ 84 if (write(ready_out, &dummy, 1) != 1) 85 barf("CLIENT: ready write"); 86 87 /* Wait for "GO" signal */ 88 if (poll(&pollfd, 1, -1) != 1) 89 barf("poll"); 90 } 91 92 /* Sender sprays loops messages down each file descriptor */ 93 static void *sender(struct sender_context *ctx) 94 { 95 char data[DATASIZE]; 96 unsigned int i, j; 97 98 ready(ctx->ready_out, ctx->wakefd); 99 100 /* Now pump to every receiver. */ 101 for (i = 0; i < loops; i++) { 102 for (j = 0; j < ctx->num_fds; j++) { 103 int ret, done = 0; 104 105 again: 106 ret = write(ctx->out_fds[j], data + done, 107 sizeof(data)-done); 108 if (ret < 0) 109 barf("SENDER: write"); 110 done += ret; 111 if (done < DATASIZE) 112 goto again; 113 } 114 } 115 116 return NULL; 117 } 118 119 120 /* One receiver per fd */ 121 static void *receiver(struct receiver_context* ctx) 122 { 123 unsigned int i; 124 125 if (!thread_mode) 126 close(ctx->in_fds[1]); 127 128 /* Wait for start... */ 129 ready(ctx->ready_out, ctx->wakefd); 130 131 /* Receive them all */ 132 for (i = 0; i < ctx->num_packets; i++) { 133 char data[DATASIZE]; 134 int ret, done = 0; 135 136 again: 137 ret = read(ctx->in_fds[0], data + done, DATASIZE - done); 138 if (ret < 0) 139 barf("SERVER: read"); 140 done += ret; 141 if (done < DATASIZE) 142 goto again; 143 } 144 145 return NULL; 146 } 147 148 static pthread_t create_worker(void *ctx, void *(*func)(void *)) 149 { 150 pthread_attr_t attr; 151 pthread_t childid; 152 int err; 153 154 if (!thread_mode) { 155 /* process mode */ 156 /* Fork the receiver. */ 157 switch (fork()) { 158 case -1: 159 barf("fork()"); 160 break; 161 case 0: 162 (*func) (ctx); 163 exit(0); 164 break; 165 default: 166 break; 167 } 168 169 return (pthread_t)0; 170 } 171 172 if (pthread_attr_init(&attr) != 0) 173 barf("pthread_attr_init:"); 174 175 #ifndef __ia64__ 176 if (pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN) != 0) 177 barf("pthread_attr_setstacksize"); 178 #endif 179 180 err = pthread_create(&childid, &attr, func, ctx); 181 if (err != 0) { 182 fprintf(stderr, "pthread_create failed: %s (%d)\n", 183 strerror(err), err); 184 exit(-1); 185 } 186 return childid; 187 } 188 189 static void reap_worker(pthread_t id) 190 { 191 int proc_status; 192 void *thread_status; 193 194 if (!thread_mode) { 195 /* process mode */ 196 wait(&proc_status); 197 if (!WIFEXITED(proc_status)) 198 exit(1); 199 } else { 200 pthread_join(id, &thread_status); 201 } 202 } 203 204 /* One group of senders and receivers */ 205 static unsigned int group(pthread_t *pth, 206 unsigned int num_fds, 207 int ready_out, 208 int wakefd) 209 { 210 unsigned int i; 211 struct sender_context *snd_ctx = malloc(sizeof(struct sender_context) 212 + num_fds * sizeof(int)); 213 214 if (!snd_ctx) 215 barf("malloc()"); 216 217 for (i = 0; i < num_fds; i++) { 218 int fds[2]; 219 struct receiver_context *ctx = malloc(sizeof(*ctx)); 220 221 if (!ctx) 222 barf("malloc()"); 223 224 225 /* Create the pipe between client and server */ 226 fdpair(fds); 227 228 ctx->num_packets = num_fds * loops; 229 ctx->in_fds[0] = fds[0]; 230 ctx->in_fds[1] = fds[1]; 231 ctx->ready_out = ready_out; 232 ctx->wakefd = wakefd; 233 234 pth[i] = create_worker(ctx, (void *)receiver); 235 236 snd_ctx->out_fds[i] = fds[1]; 237 if (!thread_mode) 238 close(fds[0]); 239 } 240 241 /* Now we have all the fds, fork the senders */ 242 for (i = 0; i < num_fds; i++) { 243 snd_ctx->ready_out = ready_out; 244 snd_ctx->wakefd = wakefd; 245 snd_ctx->num_fds = num_fds; 246 247 pth[num_fds+i] = create_worker(snd_ctx, (void *)sender); 248 } 249 250 /* Close the fds we have left */ 251 if (!thread_mode) 252 for (i = 0; i < num_fds; i++) 253 close(snd_ctx->out_fds[i]); 254 255 /* Return number of children to reap */ 256 return num_fds * 2; 257 } 258 259 static const struct option options[] = { 260 OPT_BOOLEAN('p', "pipe", &use_pipes, 261 "Use pipe() instead of socketpair()"), 262 OPT_BOOLEAN('t', "thread", &thread_mode, 263 "Be multi thread instead of multi process"), 264 OPT_UINTEGER('g', "group", &num_groups, "Specify number of groups"), 265 OPT_UINTEGER('l', "loop", &loops, "Specify number of loops"), 266 OPT_END() 267 }; 268 269 static const char * const bench_sched_message_usage[] = { 270 "perf bench sched messaging <options>", 271 NULL 272 }; 273 274 int bench_sched_messaging(int argc, const char **argv, 275 const char *prefix __used) 276 { 277 unsigned int i, total_children; 278 struct timeval start, stop, diff; 279 unsigned int num_fds = 20; 280 int readyfds[2], wakefds[2]; 281 char dummy; 282 pthread_t *pth_tab; 283 284 argc = parse_options(argc, argv, options, 285 bench_sched_message_usage, 0); 286 287 pth_tab = malloc(num_fds * 2 * num_groups * sizeof(pthread_t)); 288 if (!pth_tab) 289 barf("main:malloc()"); 290 291 fdpair(readyfds); 292 fdpair(wakefds); 293 294 total_children = 0; 295 for (i = 0; i < num_groups; i++) 296 total_children += group(pth_tab+total_children, num_fds, 297 readyfds[1], wakefds[0]); 298 299 /* Wait for everyone to be ready */ 300 for (i = 0; i < total_children; i++) 301 if (read(readyfds[0], &dummy, 1) != 1) 302 barf("Reading for readyfds"); 303 304 gettimeofday(&start, NULL); 305 306 /* Kick them off */ 307 if (write(wakefds[1], &dummy, 1) != 1) 308 barf("Writing to start them"); 309 310 /* Reap them all */ 311 for (i = 0; i < total_children; i++) 312 reap_worker(pth_tab[i]); 313 314 gettimeofday(&stop, NULL); 315 316 timersub(&stop, &start, &diff); 317 318 switch (bench_format) { 319 case BENCH_FORMAT_DEFAULT: 320 printf("# %d sender and receiver %s per group\n", 321 num_fds, thread_mode ? "threads" : "processes"); 322 printf("# %d groups == %d %s run\n\n", 323 num_groups, num_groups * 2 * num_fds, 324 thread_mode ? "threads" : "processes"); 325 printf(" %14s: %lu.%03lu [sec]\n", "Total time", 326 diff.tv_sec, 327 (unsigned long) (diff.tv_usec/1000)); 328 break; 329 case BENCH_FORMAT_SIMPLE: 330 printf("%lu.%03lu\n", diff.tv_sec, 331 (unsigned long) (diff.tv_usec/1000)); 332 break; 333 default: 334 /* reaching here is something disaster */ 335 fprintf(stderr, "Unknown format:%d\n", bench_format); 336 exit(1); 337 break; 338 } 339 340 return 0; 341 } 342