]> git.ipfire.org Git - thirdparty/binutils-gdb.git/blob - gprofng/testsuite/gprofng.display/mttest/mttest.c
gprofng: a new GNU profiler
[thirdparty/binutils-gdb.git] / gprofng / testsuite / gprofng.display / mttest / mttest.c
1 /* Copyright (C) 2021 Free Software Foundation, Inc.
2 Contributed by Oracle.
3
4 This file is part of GNU Binutils.
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3, or (at your option)
9 any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, 51 Franklin Street - Fifth Floor, Boston,
19 MA 02110-1301, USA. */
20
21 /* mttest -- show threaded use of global and local locks */
22
23 #include <sys/types.h>
24 #include <sys/stat.h>
25 #include <sys/time.h>
26 #include <sys/ipc.h>
27 #include <sys/sem.h>
28 #include <sys/sysinfo.h>
29 #include <sys/procfs.h>
30 #include <sys/fcntl.h>
31 #include <stdio.h>
32 #include <malloc.h>
33 #include <string.h>
34 #include <strings.h>
35 #include <stdlib.h>
36 #include <math.h>
37 #include <unistd.h>
38 #include <fcntl.h>
39 #include <assert.h>
40 #include <pthread.h>
41 #include <semaphore.h>
42 #include <errno.h>
43
44 #ifdef CLONE
45 #include <linux/sched.h>
46 #include <signal.h>
47 #include <sys/wait.h>
48 #include <sys/syscall.h>
49 #include <sys/mman.h>
50 #include <linux/futex.h>
51 #include <linux/unistd.h>
52 static int CLONE_FLAGS[] = {
53 CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_SYSVSEM | SIGCHLD | CLONE_CHILD_CLEARTID | CLONE_PARENT_SETTID | CLONE_IO,
54 CLONE_VM | SIGCHLD | CLONE_CHILD_CLEARTID | CLONE_PARENT_SETTID,
55 CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD | CLONE_SYSVSEM | SIGCHLD | CLONE_CHILD_CLEARTID | CLONE_PARENT_SETTID | CLONE_IO,
56 CLONE_VM | CLONE_SIGHAND | CLONE_THREAD | SIGCHLD | CLONE_CHILD_CLEARTID | CLONE_PARENT_SETTID
57 };
58
59 #define CLONE_STACK_SIZE 8388608
60 #define CLONE_TLS_SIZE 4096
61 #define CLONE_RED_SIZE 4096
62
63 #endif /* CLONE */
64
65 typedef int processorid_t;
66 typedef long long hrtime_t;
67 typedef struct timespec timespec_t;
68 extern hrtime_t gethrtime ();
69 extern hrtime_t gethrvtime ();
70
71 timespec_t * hrt_to_ts (hrtime_t hrt);
72 static const pthread_mutex_t mutex_initializer = PTHREAD_MUTEX_INITIALIZER;
73 #ifdef CLONE
74 #define CLONE_IO 0x80000000 /* Clone io context */
75 char *model = "Cloned threads";
76 #else
77 #ifdef BOUND
78 char *model = "Bound Posix threads";
79 #else
80 char *model = "Unbound Posix threads";
81 #endif
82 #endif
83
84 char *prtime (time_t *);
85 int get_clock_rate (void);
86 int get_ncpus ();
87
88 #ifdef SELFTEST
89 void start_prof (void);
90 void finish_prof (void);
91 #endif
92
93 #define _STRUCTURED_PROC 1
94 #define TRUE 1
95 #define FALSE 0
96 #define NUM_OF_THREADS 4
97 #define NUM_OF_BLOCKS 4
98 #define NUM_OF_RESOURCES 3
99 #define MYTIMEOUT 1000000000
100 #define MYDBLTIMEOUT ((double) 1000000000.)
101
102 int repeat_count = 1; /* number of times to repeat test */
103 int job_index = -1; /* index of selected job, if just one */
104 int uniprocessor = 0; /* non-zero if -u specified; causes single processor bind */
105 processorid_t cpuid;
106 processorid_t ocpuid;
107
108 // not a typedef; simplifies analyzer data display output
109 #define workCtr_t double
110
111 typedef struct workStruct_t
112 {
113 workCtr_t sum_ctr;
114 } workStruct_t;
115
116 struct Workblk;
117
118 typedef struct Workblk
119 {
120 int index; /* index of this block */
121 int strategy; /* specifies type of locking to do */
122 int proffail; /* flag set if thread loses interrupts */
123 #ifdef CLONE
124 pid_t tid; /* Linux kernel thread id */
125 #else
126 pthread_t tid; /* thread processing buffer */
127 #endif
128 pthread_mutex_t lock; /* lock for this buffer */
129 lwpid_t ilwpid; /* lwp processing buffer (initially) */
130 lwpid_t lwpid; /* lwp processing buffer (after sync) */
131
132 /* timers */
133 hrtime_t start; /* buffer fetched, wall clock */
134 hrtime_t vstart; /* buffer fetched, CPU timer */
135 hrtime_t ready; /* lock acquired (if needed), wall clock */
136 hrtime_t vready; /* lock acquired (if needed), CPU timer */
137 hrtime_t done; /* work done, wall clock */
138 hrtime_t vdone; /* work done, CPU timer */
139 hrtime_t compute_ready; /* compute ready, wall clock */
140 hrtime_t compute_vready; /* compute ready, CPU timer */
141 hrtime_t compute_done; /* compute done, wall clock */
142 hrtime_t compute_vdone; /* compute done, CPU timer */
143 struct Workblk *next; /* for queue management */
144 workStruct_t list[100];
145 } Workblk;
146
147 /* lookup table for behavior scripts */
148 struct scripttab
149 {
150 char *test_name;
151 void (*test_func)(Workblk *, struct scripttab *);
152 char *called_name;
153 void (*called_func)(workStruct_t *);
154 };
155
156 int locktest ();
157 void resolve_symbols ();
158 void init_micro_acct ();
159 void compute_set (volatile workStruct_t *x);
160 void compute (workStruct_t *x);
161 void computeA (workStruct_t *x);
162 void computeB (workStruct_t *x);
163 void computeC (workStruct_t *x);
164 void computeD (workStruct_t *x);
165 void computeE (workStruct_t *x);
166 void computeF (workStruct_t *x);
167 void computeG (workStruct_t *x);
168 void computeH (workStruct_t *x);
169 void computeI (workStruct_t *x);
170 void computeJ (workStruct_t *x);
171 void computeK (workStruct_t *x);
172 void addone (workCtr_t *x);
173 void init_arrays (int strat);
174 void dump_arrays ();
175 void *do_work (void *v);
176 void thread_work ();
177 void nothreads (Workblk *array, struct scripttab *k);
178 void lock_none (Workblk *array, struct scripttab *k);
179 void cache_trash (Workblk *array, struct scripttab *k);
180 void lock_global (Workblk *array, struct scripttab *k);
181 void trylock_global (Workblk *array, struct scripttab *k);
182 void lock_local (Workblk *array, struct scripttab *k);
183 void calladd (Workblk *array, struct scripttab *k);
184 void cond_global (Workblk *array, struct scripttab *k);
185 void cond_timeout_global (Workblk *array, struct scripttab *k);
186 void sema_global (Workblk *array, struct scripttab *k);
187 void read_write (Workblk *array, struct scripttab *k);
188 void s5sem (Workblk *array, struct scripttab *k);
189 FILE *open_output (char *filename);
190 int close_file (FILE *f);
191 void scale_init (int argcc, char **argvv);
192 void
193 Print_Usage (int);
194
195 struct scripttab scripttab[] = {
196 #ifdef CLONE
197 {"nothreads", nothreads, "compute", compute},
198 {"lock_none", lock_none, "computeA", computeA},
199 {"cache_trash", cache_trash, "computeB", computeB},
200 {"calladd", calladd, "computeF", computeF},
201 {"sema_global", sema_global, "computeI", computeI},
202 #else
203 {"nothreads", nothreads, "compute", compute},
204 {"cond_timeout_global", cond_timeout_global, "computeH", computeH},
205 {"lock_none", lock_none, "computeA", computeA},
206 {"cache_trash", cache_trash, "computeB", computeB},
207 {"lock_global", lock_global, "computeC", computeC},
208 {"trylock_global", trylock_global, "computeD", computeD},
209 {"lock_local", lock_local, "computeE", computeE},
210 {"calladd", calladd, "computeF", computeF},
211 {"sema_global", sema_global, "computeI", computeI},
212 {"cond_global", cond_global, "computeG", computeG},
213 #endif
214 {NULL, NULL, NULL, NULL}
215 };
216
217 static pthread_mutex_t global_lock = PTHREAD_MUTEX_INITIALIZER;
218 static pthread_mutex_t global_cond_lock = PTHREAD_MUTEX_INITIALIZER;
219 static pthread_mutex_t global_cond_lock2 = PTHREAD_MUTEX_INITIALIZER;
220 static pthread_cond_t global_cond = PTHREAD_COND_INITIALIZER;
221 static timespec_t time_out;
222 static sem_t global_sema_lock; /* dynamically initted */
223 static int s5_sema_id;
224 static int global_cond_flag = TRUE;
225 static int count = NUM_OF_RESOURCES;
226
227 /* an array of workStruct_ts that is contiguous */
228 workStruct_t *element;
229
230 typedef struct
231 {
232 int size;
233 Workblk *arrays;
234 } Head;
235
236 int nthreads = NUM_OF_THREADS;
237 int narrays = NUM_OF_BLOCKS;
238 static Head head;
239 char *name;
240 FILE *fid;
241
242 #ifdef CLONE
243 static sem_t fetch_sema_lock;
244 static pid_t *tid;
245 static void *stack_space[NUM_OF_THREADS];
246 static void *stack[NUM_OF_THREADS];
247 int stack_size = CLONE_STACK_SIZE;
248 #else
249 static pthread_t *tid;
250 #endif
251 pthread_attr_t attr;
252
253 int
254 main (int argc, char **argv, char **envp)
255 {
256 int i;
257 scale_init (argc, argv);
258
259 #define ALIGNMENTOFFSET 2 /* adjust alignment */
260 i = sizeof (workStruct_t) * (narrays + ALIGNMENTOFFSET);
261 element = memalign (64, i);
262 if (element == NULL)
263 {
264 perror ("calloc( narrays, sizeof(workStruct_t) )");
265 exit (1);
266 }
267 compute_set (element);
268 memset (element, 0, i);
269 element += ALIGNMENTOFFSET;
270
271 #ifdef SELFTEST
272 start_prof ();
273 #endif
274 fid = open_output ("mttest.acct");
275 if (job_index == -1)
276 i = (sizeof (scripttab) / sizeof ( struct scripttab) - 1);
277 else
278 i = 1;
279 fprintf (fid, "Number of tests: %d Repeat count: %d\n", i, repeat_count);
280 fprintf (fid, "MHz: %d\n", get_clock_rate ());
281 fprintf (fid, "X Incl. Total Incl. CPU Incl. Sync. Wait Name (%s)\n",
282 model);
283 fprintf (fid, "X %7.3f %7.3f %7.3f %s\n",
284 0.0, 0.0, 0.0, "<Unknown>");
285 fflush (fid);
286 name = strdup (argv[0]);
287 init_micro_acct ();
288 pthread_attr_init (&attr);
289
290 #ifdef BOUND
291 pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM);
292 #endif
293 sem_init (&global_sema_lock, 0, count);
294 #ifdef CLONE
295 sem_init (&fetch_sema_lock, 0, 1);
296 for (i = 0; i < nthreads; i++)
297 {
298 stack_space[i] = mmap (NULL, stack_size, PROT_READ | PROT_WRITE
299 | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS | MAP_GROWSDOWN, -1, 0);
300 if ((void*) - 1 == stack_space[i])
301 {
302 fprintf (stderr, "Error: mmap returned -1\n");
303 exit (1);
304 }
305 mprotect (stack_space[i], CLONE_RED_SIZE, PROT_NONE);
306 stack[i] = (char*) (stack_space[i]) + stack_size - CLONE_TLS_SIZE; // stack grows back
307 }
308 #endif
309
310 resolve_symbols ();
311 i = locktest ();
312 close_file (fid);
313
314 #ifdef SELFTEST
315 finish_prof ();
316 #endif
317 return 0;
318 }
319
320 Workblk *in_queue = NULL;
321 Workblk *in_queue_last = NULL;
322
323 pthread_mutex_t queue_lock;
324
325 void
326 queue_work (Workblk * w)
327 {
328 if (in_queue == NULL)
329 {
330 in_queue = w;
331 in_queue_last = w;
332 }
333 else
334 {
335 in_queue_last->next = w;
336 in_queue_last = w;
337 }
338 }
339
340 Workblk *
341 fetch_work ()
342 {
343 /* acquire the queue lock */
344 #ifdef CLONE
345 sem_wait (&fetch_sema_lock);
346 #else
347 pthread_mutex_lock (&queue_lock);
348 #endif
349
350 /* get the next block */
351 Workblk *w = in_queue;
352 if (w != NULL)
353 {
354 in_queue = w->next;
355 w->next = NULL;
356 if (in_queue == NULL)
357 in_queue_last = NULL;
358 }
359 #ifdef CLONE
360 sem_post (&fetch_sema_lock);
361 #else
362 pthread_mutex_unlock (&queue_lock);
363 #endif
364
365 /* return the block */
366 return w;
367 }
368
369 int
370 locktest ()
371 {
372 int i;
373 Workblk *array;
374 struct scripttab *k;
375 hrtime_t start;
376 hrtime_t vstart;
377 hrtime_t end;
378 hrtime_t vend;
379 struct timeval ttime;
380 time_t secs;
381
382 head.size = narrays;
383 head.arrays = (Workblk *) calloc (narrays, sizeof (Workblk));
384
385 for (i = 0, array = head.arrays; i < narrays; i++, array++)
386 array->index = i;
387
388 printf ("%s: number of %s = %d, number of blocks = %d, repeat %d times %s\n",
389 name, model, nthreads, narrays, repeat_count,
390 (uniprocessor == 0 ? "" : "[single CPU]"));
391 #ifdef CLONE
392 tid = (pid_t *) calloc (nthreads*repeat_count, sizeof (pid_t));
393 #else
394 tid = (pthread_t *) calloc (nthreads*repeat_count, sizeof (pthread_t));
395 #endif
396 for (count = 0; count < repeat_count; count++)
397 {
398 (void) gettimeofday (&ttime, NULL);
399 secs = (time_t) ttime.tv_sec;
400 printf ("Iteration %d, starting %s\n", count + 1, prtime (&secs));
401 if (job_index == -1)
402 {
403 for (i = 0;; i++)
404 {
405 k = &scripttab[i];
406 if (k->test_name == NULL)
407 break;
408
409 printf ("begin thread_work, %s\n", k->test_name);
410 init_arrays (i);
411 start = gethrtime ();
412 vstart = gethrvtime ();
413
414 if (strcmp (k->test_name, "nothreads") == 0)
415 {
416 /* the "nothreads" task is special-cased to run in the main thread */
417 int one_thread = 1;
418 do_work (&one_thread);
419 }
420 else if (nthreads == 1)
421 {
422 int one_thread = 1;
423 do_work (&one_thread);
424 }
425 else
426 thread_work ();
427 end = gethrtime ();
428 vend = gethrvtime ();
429 dump_arrays (end - start, vend - vstart, i);
430 }
431 }
432 else
433 {
434 k = &scripttab[job_index];
435 if (k->test_name == NULL)
436 break;
437
438 printf ("begin thread_work, %s\n", k->test_name);
439 init_arrays (job_index);
440 start = gethrtime ();
441 vstart = gethrvtime ();
442 if (strcmp (k->test_name, "nothreads") == 0)
443 {
444 /* first one is special-cased to run in 1 thread */
445 int one_thread = 1;
446 do_work (&one_thread);
447 }
448 else if (nthreads == 1)
449 do_work (NULL);
450 else
451 thread_work ();
452 end = gethrtime ();
453 vend = gethrvtime ();
454 dump_arrays (end - start, vend - vstart, job_index);
455 }
456 }
457
458 /* we're done, return */
459 return (0);
460 }
461
462 void
463 init_arrays (int strat)
464 {
465 int i;
466 Workblk *array;
467 for (i = 0, array = head.arrays; i < narrays; i++, array++)
468 {
469 bzero (array, sizeof (Workblk));
470 array->index = i;
471 array->strategy = strat;
472 queue_work (array);
473 }
474 }
475
476 void
477 dump_arrays (hrtime_t real, hrtime_t cpu, int case_index)
478 {
479 int i;
480 double t1, t2, t3, t4, t5, t6, t7, t8;
481 Workblk *array;
482 struct scripttab *k;
483 double sumtotal = 0.;
484 double sumCPU = 0.;
485 double sumlock = 0.;
486 double sumCompTotal = 0.;
487 double sumCompCPU = 0.;
488 int proffail = 0;
489 printf (" real real real CPU\n");
490 printf ("idx (t id) total lock crunch crunch\n");
491 for (i = 0, array = head.arrays; i < narrays; i++, array++)
492 {
493 /* check to see if data lost for this block */
494 /* set flag to disable the comparison */
495 /* convert times to seconds */
496 t1 = ((double) array->done - array->start) / MYDBLTIMEOUT;
497 t2 = ((double) array->vdone - array->vstart) / MYDBLTIMEOUT;
498 t3 = ((double) array->ready - array->start) / MYDBLTIMEOUT;
499 t4 = ((double) array->vready - array->vstart) / MYDBLTIMEOUT;
500 t5 = ((double) array->done - array->ready) / MYDBLTIMEOUT;
501 t6 = ((double) array->vdone - array->vready) / MYDBLTIMEOUT;
502 t7 = ((double) array->compute_done - array->compute_ready) / MYDBLTIMEOUT;
503 t8 = ((double) array->compute_vdone - array->compute_vready)
504 / MYDBLTIMEOUT;
505
506 if (array->proffail != 0)
507 proffail = 1;
508 sumtotal = sumtotal + t1; /* incl. total time */
509 sumlock = sumlock + t3; /* incl. sync. wait time */
510 #ifdef BOUND
511 /* NOTE:
512 * for bound threads, sumCPU includes the synchronization
513 * CPU time; for unbound it does not
514 */
515 sumCPU = sumCPU + t2; /* test incl. CPU time */
516 #else
517 sumCPU = sumCPU + t6; /* test incl. CPU time */
518 #endif
519 sumCompTotal = sumCompTotal + t7; /* compute incl. totaltime */
520 sumCompCPU = sumCompCPU + t8; /* compute incl. CPU time */
521 printf ("#%2d (t%3ld, il%3d, l%3d) %10.6f %10.6f %10.6f %10.6f%s\n",
522 array->index, array->tid, array->ilwpid, array->lwpid, t1, t3,
523 t5, t6, array->proffail == 0 ? "" : " *");
524 if (t4 == 0) printf ("t4 == 0\n");
525 assert (array->lwpid > 0);
526 #if defined(BOUND)
527 assert (array->lwpid == array->ilwpid);
528 #endif
529 }
530
531 k = &scripttab[case_index];
532
533 printf ("%-25s %10.6f %10.6f %-9s %10.6f\n", k->test_name, sumtotal,
534 sumlock, k->called_name, sumCPU);
535 printf ("main %10.6f\n\n",
536 (double) real / MYDBLTIMEOUT);
537
538 /* write accounting record for task */
539 fprintf (fid, "X %7.3f %7.3f %7.3f %s%s\n",
540 sumtotal, sumCPU, sumlock, k->test_name,
541 (proffail == 0 ? "" : " *"));
542 /* write accounting record for task's compute function */
543 fprintf (fid, "X %7.3f %7.3f 0. %s%s\n",
544 sumCompTotal, sumCompCPU, k->called_name,
545 (proffail == 0 ? "" : " *"));
546 fflush (fid);
547 fflush (stdout);
548
549 }
550
551 void
552 thread_work ()
553 {
554 int i;
555 #ifdef CLONE
556 pid_t ctid[NUM_OF_THREADS];
557 for (i = 0; i < nthreads; i++)
558 ctid[i] = -1;
559 #endif
560
561 /* create nthreads threads, having each start at do_work */
562 for (i = 0; i < nthreads; i++)
563 {
564 int retval;
565 #ifdef BOUND
566 retval = pthread_create (&(tid[i]), &attr, do_work, 0);
567 #endif
568 #ifdef UNBOUND
569 retval = pthread_create (&(tid[i]), 0, do_work, 0);
570 #endif
571 #ifdef CLONE
572 tid[i] = retval = clone ((int (*)(void*))do_work, stack[i],
573 CLONE_FLAGS[i % sizeof (CLONE_FLAGS)], NULL,
574 &(ctid[i]), NULL, &(ctid[i]));
575 if (retval < 0)
576 {
577 perror ("Oops, clone failed");
578 exit (1);
579 }
580 #else
581 if (retval != 0)
582 {
583 perror ("Oops, thr_create failed");
584 exit (1);
585 }
586 #endif
587 }
588
589 /* wait for all threads to complete their work and join */
590 for (i = 0; i < nthreads; i++)
591 {
592 #ifdef CLONE
593 int counter = 0;
594 while (ctid[i] == -1)
595 counter++;
596 while (ctid[i] != 0)
597 syscall (__NR_futex, &(ctid[i]), FUTEX_WAIT, tid[i], NULL);
598 #else
599 pthread_join (tid[i], 0);
600 #endif
601 }
602 #ifdef CLONE
603 for (i = 0; i < nthreads / 2; i++)
604 {
605 int status;
606 waitpid (tid[i], &status, __WALL);
607 }
608 #endif
609 }
610
611 /* do_work: process array's data with locking, based on array->strategy */
612 void *
613 do_work (void *v)
614 {
615 Workblk *array;
616 struct scripttab *k;
617 int i;
618 volatile double x;
619
620 #ifdef CLONE
621 pid_t mytid = syscall (__NR_gettid);
622 #else
623 pthread_t mytid = pthread_self ();
624 #endif
625
626 /* delay to ensure that a tick passes, so that the
627 * first profile packet doesn't show the thread startup time
628 * attributed to the accounting functions
629 */
630 x = 0;
631 for (i = 0; i < 2000000; i++)
632 x = x + 1.0;
633
634 for (;;)
635 {
636 /* fetch a workblk */
637 array = fetch_work ();
638 if (array == NULL) /* we're done */
639 break;
640 array->lock = mutex_initializer;
641 array->proffail = 0;
642 array->tid = mytid;
643 array->ilwpid = getpid () /* pthread_self()*/;
644
645 array->lwpid = -1; /* initialize to inappropriate value */
646 array->start = gethrtime ();
647 array->vstart = gethrvtime ();
648
649 k = &scripttab[array->strategy];
650 (k->test_func)(array, k);
651
652 array->done = gethrtime ();
653 array->vdone = gethrvtime ();
654 array->lwpid = getpid () /* pthread_self()*/;
655
656 #if defined(BOUND)
657 assert (array->lwpid == array->ilwpid);
658 #endif
659 }
660
661 #ifdef CLONE
662 if (v == NULL)
663 syscall (__NR_exit);
664 #endif
665 return NULL;
666 }
667
668 /* nothreads: process array's data with no locking; called without threads */
669 void
670 nothreads (Workblk *array, struct scripttab *k)
671 {
672 array->ready = gethrtime ();
673 array->vready = gethrvtime ();
674 array->compute_ready = array->ready;
675 array->compute_vready = array->vready;
676
677 /* do some work on the current array */
678 (k->called_func)(&array->list[0]);
679
680 array->compute_done = gethrtime ();
681 array->compute_vdone = gethrvtime ();
682
683 }
684
685 /* lock_none: process array's data with no locking */
686 void
687 lock_none (Workblk *array, struct scripttab *k)
688 {
689 array->ready = array->start;
690 array->vready = array->vstart;
691 array->compute_ready = array->ready;
692 array->compute_vready = array->vready;
693
694 /* do some work on the current array */
695 (k->called_func)(&array->list[0]);
696
697 array->compute_done = gethrtime ();
698 array->compute_vdone = gethrvtime ();
699
700 }
701
702 /* cache_trash_even:
703 * called for even numbered l1 cache lines
704 */
705 void
706 cache_trash_even (Workblk *array, struct scripttab *k)
707 {
708 /* use a datum that will share a cache line with others */
709 (k->called_func)(&element[array->index]);
710 }
711
712 /* cache_trash_odd:
713 * called for odd numbered l1 cache lines
714 */
715 void
716 cache_trash_odd (Workblk *array, struct scripttab *k)
717 {
718 /* use a datum that will share a cache line with others */
719 (k->called_func)(&element[array->index]);
720 }
721
722 /* cache_trash: multiple threads refer to adjacent words,
723 * causing false sharing of cache lines, and trashing
724 */
725 void
726 cache_trash (Workblk *array, struct scripttab *k)
727 {
728 array->ready = array->start;
729 array->vready = array->vstart;
730 array->compute_ready = array->ready;
731 array->compute_vready = array->vready;
732
733 /* use a datum that will share a cache line with others */
734 if ((unsigned long) (&element[array->index]) / 32 & 1)
735 cache_trash_odd (array, k);
736 else
737 cache_trash_even (array, k);
738
739 array->compute_done = gethrtime ();
740 array->compute_vdone = gethrvtime ();
741 }
742
743 /* lock_global: use a global lock to process array's data */
744 void
745 lock_global (Workblk *array, struct scripttab *k)
746 {
747 /* acquire the global lock */
748 pthread_mutex_lock (&global_lock);
749
750 array->ready = gethrtime ();
751 array->vready = gethrvtime ();
752 array->compute_ready = array->ready;
753 array->compute_vready = array->vready;
754
755 /* do some work on the current array */
756 (k->called_func)(&array->list[0]);
757
758 array->compute_done = gethrtime ();
759 array->compute_vdone = gethrvtime ();
760
761 /* free the global lock */
762 pthread_mutex_unlock (&global_lock);
763 /* make another call to preclude tail-call optimization on the unlock */
764 (void) gethrtime ();
765 }
766
767 /* trylock_global: busy-wait on a global lock to process array's data */
768 void
769 trylock_global (Workblk *array, struct scripttab *k)
770 {
771 int ret;
772
773 /* set ready before starting, since this is a busy wait */
774 array->ready = gethrtime ();
775 array->vready = gethrvtime ();
776
777 /* busy wait to acquire the global lock */
778 do
779 {
780 ret = pthread_mutex_trylock (&global_lock);
781 }
782 while (ret == EBUSY);
783 array->compute_ready = gethrtime ();
784 array->compute_vready = gethrvtime ();
785
786 /* do some work on the current array */
787 (k->called_func)(&array->list[0]);
788
789 array->compute_done = gethrtime ();
790 array->compute_vdone = gethrvtime ();
791
792 /* free the global lock */
793 pthread_mutex_unlock (&global_lock);
794 /* make another call to preclude tail-call optimization on the unlock */
795 (void) gethrtime ();
796 }
797
798 /* lock_local: use a local lock to process array's data */
799 void
800 lock_local (Workblk *array, struct scripttab *k)
801 {
802 /* acquire the local lock */
803 pthread_mutex_lock (&(array->lock));
804 array->ready = gethrtime ();
805 array->vready = gethrvtime ();
806 array->compute_ready = array->ready;
807 array->compute_vready = array->vready;
808
809 /* do some work on the current array */
810 (k->called_func)(&array->list[0]);
811
812 array->compute_done = gethrtime ();
813 array->compute_vdone = gethrvtime ();
814
815 /* free the local lock */
816 pthread_mutex_unlock (&array->lock);
817 /* make another call to preclude tail-call optimization on the unlock */
818 (void) gethrtime ();
819 }
820
821 /* cond_global: use a global condition variable to process array's data */
822 void
823 cond_global (Workblk *array, struct scripttab *k)
824 {
825 /* acquire the global condition lock */
826 pthread_mutex_lock (&global_cond_lock);
827
828 /* check to see if the condition flag is true, If not then wait
829 for that condition flag to become true. */
830 while (global_cond_flag != TRUE)
831 pthread_cond_wait (&global_cond, &global_cond_lock);
832 /* Now, condition is true, and we have the global_cond_lock */
833
834 /* set the condition flag to be FALSE, so when a new thread
835 * is created, it should wait till this one is done.
836 */
837 global_cond_flag = FALSE;
838
839 /* free the global_cond_lock and acquire the global lock */
840 pthread_mutex_unlock (&global_cond_lock);
841 pthread_mutex_lock (&global_lock);
842
843 array->ready = gethrtime ();
844 array->vready = gethrvtime ();
845
846 array->compute_ready = array->ready;
847 array->compute_vready = array->vready;
848
849 /* do some work on the current array */
850 (k->called_func)(&array->list[0]);
851
852 array->compute_done = gethrtime ();
853 array->compute_vdone = gethrvtime ();
854
855 /* free the global lock */
856 pthread_mutex_unlock (&global_lock);
857
858 /* now set the condition, and signal any other threads */
859 pthread_mutex_lock (&global_cond_lock);
860
861 global_cond_flag = TRUE;
862 pthread_cond_signal (&global_cond);
863 pthread_mutex_unlock (&global_cond_lock);
864 /* make another call to preclude tail-call optimization on the unlock */
865 (void) gethrtime ();
866 }
867
868 /* cond_timeout_global: use a global condition time wait variable to
869 process array's data */
870 void
871 cond_timeout_global (Workblk *array, struct scripttab *k)
872 {
873 int err;
874 struct timeval current_time;
875
876 /* acquire the global condition lock */
877 pthread_mutex_lock (&global_cond_lock);
878 gettimeofday (&current_time, NULL);
879 time_out.tv_sec = current_time.tv_sec;
880 time_out.tv_nsec = current_time.tv_usec * 1000;
881
882 /* check to see if the condition flag is true, If not then wait
883 * for that condition flag to become true
884 */
885
886 while (global_cond_flag != TRUE)
887 {
888 /* add MYTIMEOUT to current time for timeout */
889 time_out.tv_nsec += MYTIMEOUT;
890 while (time_out.tv_nsec > 1000000000)
891 {
892 time_out.tv_nsec -= 1000000000;
893 time_out.tv_sec++;
894 }
895 err = pthread_cond_timedwait (&global_cond, &global_cond_lock, &time_out);
896 if (err == 0)
897 break;
898 }
899 /* Now, condition is true, and we have the global_cond_lock */
900
901 pthread_mutex_unlock (&global_cond_lock);
902
903 pthread_mutex_lock (&global_cond_lock2);
904 global_cond_flag = FALSE;
905 pthread_mutex_unlock (&global_cond_lock2);
906
907 /* acquire the global lock */
908 pthread_mutex_lock (&global_lock);
909
910 array->ready = gethrtime ();
911 array->vready = gethrvtime ();
912
913 array->compute_ready = array->ready;
914 array->compute_vready = array->vready;
915
916 /* do some work on the current array */
917 (k->called_func)(&array->list[0]);
918 array->compute_done = gethrtime ();
919 array->compute_vdone = gethrvtime ();
920
921 /* free the global lock */
922 pthread_mutex_unlock (&global_lock);
923
924 /* now set the condition, and signal any other threads */
925 pthread_mutex_lock (&global_cond_lock2);
926
927 global_cond_flag = TRUE;
928 pthread_cond_signal (&global_cond);
929 pthread_mutex_unlock (&global_cond_lock2);
930
931 /* make another call to preclude tail-call optimization on the unlock */
932 (void) gethrtime ();
933 }
934
935 /* read_write: use a global Reader/Writer lock to process array's data */
936 void
937 read_write (Workblk *array, struct scripttab *k)
938 {
939 /* make another call to preclude tail-call optimization on the unlock */
940 (void) gethrtime ();
941 }
942
943 /* sema_global: use a global semaphore to process array's data */
944 void
945 sema_global (Workblk *array, struct scripttab *k)
946 {
947 sem_wait (&global_sema_lock);
948 array->ready = gethrtime ();
949 array->vready = gethrvtime ();
950 array->compute_ready = array->ready;
951 array->compute_vready = array->vready;
952
953 /* do some work on the current array */
954 (k->called_func)(&array->list[0]);
955
956 array->compute_done = gethrtime ();
957 array->compute_vdone = gethrvtime ();
958 sem_post (&global_sema_lock);
959
960 /* make another call to preclude tail-call optimization on the unlock */
961 (void) gethrtime ();
962 }
963
964 /* s5sema: use a global UNIX System V semaphore to process array's data */
965 void
966 s5sem (Workblk *array, struct scripttab *k)
967 {
968 static struct sembuf op_wait[] = {
969 { 0, -1, IPC_NOWAIT}
970 };
971 static struct sembuf op_post[] = {
972 { 0, 1, 0}
973 };
974 int sema_val;
975
976 /* set ready before starting, since this is a busy wait */
977 array->ready = gethrtime ();
978 array->vready = gethrvtime ();
979 do
980 {
981 sema_val = semop (s5_sema_id, op_wait, 1);
982 }
983 while (sema_val == -1);
984
985 array->compute_ready = gethrtime ();
986 array->compute_vready = gethrvtime ();
987
988 /* do some work on the current array */
989 (k->called_func)(&array->list[0]);
990
991 array->compute_done = gethrtime ();
992 array->compute_vdone = gethrvtime ();
993
994 if (semop (s5_sema_id, op_post, 1) == -1)
995 perror ("semop: post");
996 /* make another call to preclude tail-call optimization on the unlock */
997 (void) gethrtime ();
998 }
999
1000 /* lock_local: use a local lock to process array's data */
1001 void
1002 calladd (Workblk *array, struct scripttab *k)
1003 {
1004 array->ready = array->start;
1005 array->vready = array->vstart;
1006 array->compute_ready = array->ready;
1007 array->compute_vready = array->vready;
1008 (k->called_func)(&array->list[0]);
1009 array->compute_done = gethrtime ();
1010 array->compute_vdone = gethrvtime ();
1011 }
1012
1013 /* compute*: several copies, each burns cpu time, incrementing a workStruct_t */
1014 static long long loop_count = 80000000;
1015
1016 void
1017 compute_set (volatile workStruct_t *x)
1018 {
1019 double testtime = 3.0;
1020 char *s = getenv ("SP_COLLECTOR_TEST_TIMER");
1021 if (s)
1022 {
1023 testtime = atof (s);
1024 if (testtime < 1.0)
1025 testtime = 1.0;
1026 }
1027 hrtime_t t = gethrtime ();
1028 x->sum_ctr = 0;
1029 loop_count = 10000;
1030 for (long long i = 0; i < loop_count; i++)
1031 x->sum_ctr = x->sum_ctr + 1.0;
1032 t = gethrtime () - t;
1033 loop_count *= testtime * 1e9 / t;
1034 printf ("compute_set: loop_count=%lld\n", loop_count);
1035 }
1036
1037 void
1038 compute (workStruct_t *x)
1039 {
1040 x->sum_ctr = 0;
1041 for (long long i = 0; i < loop_count; i++)
1042 x->sum_ctr = x->sum_ctr + 1.0;
1043 }
1044
1045 void
1046 computeA (workStruct_t *x)
1047 {
1048 x->sum_ctr = 0;
1049 for (long long i = 0; i < loop_count; i++)
1050 x->sum_ctr = x->sum_ctr + 1.0;
1051 }
1052
1053 void
1054 computeB (workStruct_t *x)
1055 {
1056 x->sum_ctr = 0;
1057 for (long long i = 0; i < loop_count; i++)
1058 x->sum_ctr = x->sum_ctr + 1.0;
1059 }
1060
1061 void
1062 computeC (workStruct_t *x)
1063 {
1064 x->sum_ctr = 0;
1065 for (long long i = 0; i < loop_count; i++)
1066 x->sum_ctr = x->sum_ctr + 1.0;
1067 }
1068
1069 void
1070 computeD (workStruct_t *x)
1071 {
1072 x->sum_ctr = 0;
1073 for (long long i = 0; i < loop_count; i++)
1074 x->sum_ctr = x->sum_ctr + 1.0;
1075 }
1076
1077 void
1078 computeE (workStruct_t *x)
1079 {
1080 x->sum_ctr = 0;
1081 for (long long i = 0; i < loop_count; i++)
1082 x->sum_ctr = x->sum_ctr + 1.0;
1083 }
1084
1085 /* note that this one is different from the others, in that it calls
1086 * a function to do the add
1087 */
1088 void
1089 computeF (workStruct_t *x)
1090 {
1091 x->sum_ctr = 0;
1092 for (long long i = 0; i < loop_count; i++)
1093 addone (&x->sum_ctr);
1094 }
1095
1096 void
1097 computeG (workStruct_t *x)
1098 {
1099 x->sum_ctr = 0;
1100 for (long long i = 0; i < loop_count; i++)
1101 x->sum_ctr = x->sum_ctr + 1.0;
1102 }
1103
1104 void
1105 computeH (workStruct_t *x)
1106 {
1107 x->sum_ctr = 0;
1108 for (long long i = 0; i < loop_count; i++)
1109 x->sum_ctr = x->sum_ctr + 1.0;
1110 }
1111
1112 void
1113 computeI (workStruct_t *x)
1114 {
1115 x->sum_ctr = 0;
1116 for (long long i = 0; i < loop_count; i++)
1117 x->sum_ctr = x->sum_ctr + 1.0;
1118 }
1119
1120 void
1121 computeJ (workStruct_t *x)
1122 {
1123 x->sum_ctr = 0;
1124 for (long long i = 0; i < loop_count; i++)
1125 x->sum_ctr = x->sum_ctr + 1.0;
1126 }
1127
1128 void
1129 computeK (workStruct_t *x)
1130 {
1131 x->sum_ctr = 0;
1132 for (long long i = 0; i < loop_count; i++)
1133 x->sum_ctr = x->sum_ctr + 1.0;
1134 }
1135
1136 void
1137 addone (workCtr_t *x)
1138 {
1139 *x = *x + 1.0;
1140 }
1141
1142 FILE *
1143 open_output (char *filename)
1144 {
1145 errno = 0;
1146 FILE *f = fopen (filename, "w");
1147 if (f == NULL)
1148 fprintf (stderr, "Open of %s for output failed: %s\n",
1149 filename, strerror (errno));
1150 return f;
1151 }
1152
1153 int
1154 close_file (FILE *f)
1155 {
1156 if (f == NULL)
1157 return 0;
1158 errno = 0;
1159 int s = fclose (f);
1160 if (s == EOF)
1161 perror ("Close failed");
1162 return s;
1163 }
1164
1165 void
1166 scale_init (int argcc, char **argvv)
1167 {
1168 int num;
1169 int ii;
1170 char *p;
1171 struct scripttab *kk;
1172
1173 if (argcc >= 2) /* run mttest with options */
1174 {
1175 for (int i = 1; i < argcc; i++)
1176 {
1177 int j = i;
1178 if (argvv[i][0] != '-')
1179 Print_Usage (1);
1180 if (argvv[i][1] == 'h' || argvv[i][1] == 'H')
1181 Print_Usage (0);
1182 if (argvv[i][1] == 'u')
1183 {
1184 uniprocessor++;
1185 continue;
1186 }
1187 if (strlen (argvv[i]) == 2)
1188 {
1189 /* argument has blank separating key and number */
1190 j++;
1191 if (argcc > j)
1192 {
1193 p = argvv[j];
1194 num = atoi (p);
1195 }
1196 else
1197 Print_Usage (1);
1198 }
1199 else
1200 {
1201 /* argument has no blank separating key and number */
1202 p = argvv[i] + 2;
1203 num = atoi (p);
1204 }
1205
1206 switch (argvv[i][1])
1207 {
1208 case 't':
1209 case 'T':
1210 nthreads = num;
1211 break;
1212 case 'b':
1213 case 'B':
1214 narrays = num;
1215 break;
1216 case 'r':
1217 case 'R':
1218 repeat_count = num;
1219 break;
1220 case 'j':
1221 case 'J':
1222 /* argument is a job name; p points to string */
1223 for (ii = 0;; ii++)
1224 {
1225 kk = &scripttab[ii];
1226 if (kk->test_name == NULL) /* Oops, name not found */
1227 Print_Usage (2);
1228 if (strcmp (kk->test_name, p) == 0) /* found it */
1229 break;
1230 }
1231 job_index = ii;
1232 break;
1233 default:
1234 Print_Usage (1);
1235 }
1236 i = j;
1237 }
1238 }
1239 }
1240
1241 void
1242 Print_Usage (int error)
1243 {
1244 if (error == 1)
1245 printf ("\nError: Incorrect option\n");
1246 else if (error == 2)
1247 printf ("\nError: job name not found\n");
1248 printf ("Usage: mttest [-t num_of_threads] [-b num_of_blocks] "
1249 "[-R repeat_count] [-u] [-j job_name]\n");
1250 printf (" -u implies binding all LWPs to one CPU with processor_bind\n");
1251 printf (" job_name is one of:\n");
1252 for (int ii = 0;; ii++)
1253 {
1254 struct scripttab *kk = &scripttab[ii];
1255 if (kk->test_name == NULL)
1256 break;
1257 printf ("\t%s\n", kk->test_name);
1258 }
1259 printf (" if job_name is omitted, each will be run in turn\n");
1260 exit (-1);
1261 }
1262
1263 void
1264 resolve_symbols ()
1265 {
1266 global_cond_flag = TRUE;
1267 pthread_mutex_lock (&queue_lock);
1268 pthread_mutex_trylock (&queue_lock);
1269 pthread_mutex_unlock (&queue_lock);
1270 sem_post (&global_sema_lock);
1271 sem_wait (&global_sema_lock);
1272 #ifdef CLONE
1273 sem_post (&fetch_sema_lock);
1274 sem_wait (&fetch_sema_lock);
1275 #endif
1276 }
1277
1278 /* prtime (ttime)
1279 * returns a pointer to a static string in the form:
1280 * Thu 01 Jan 00 00:00:00\0
1281 * 01234567890122345678901234
1282 * ttime is a pointer to a UNIX time in seconds since epoch
1283 * library routine localtime() is used
1284 */
1285 char *
1286 prtime (time_t *ttime)
1287 {
1288 static char *days[] = {
1289 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
1290 };
1291 static char *months[] = {
1292 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
1293 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
1294 };
1295
1296 static char cvbuf[26];
1297
1298 /* get the date and time */
1299 struct tm *tp = localtime (ttime);
1300
1301 /* convert to string */
1302 sprintf (cvbuf, "%3s %02d %s %02d %02d:%02d:%02d",
1303 days[tp->tm_wday], tp->tm_mday, months[tp->tm_mon],
1304 tp->tm_year % 100, tp->tm_hour, tp->tm_min, tp->tm_sec);
1305 return cvbuf;
1306 }