1 /* Copyright (C) 2002-2022 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3 
4    The GNU C Library is free software; you can redistribute it and/or
5    modify it under the terms of the GNU Lesser General Public
6    License as published by the Free Software Foundation; either
7    version 2.1 of the License, or (at your option) any later version.
8 
9    The GNU C Library is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12    Lesser General Public License for more details.
13 
14    You should have received a copy of the GNU Lesser General Public
15    License along with the GNU C Library; if not, see
16    <https://www.gnu.org/licenses/>.  */
17 
18 #define _GNU_SOURCE	1
19 #include <argp.h>
20 #include <error.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <inttypes.h>
24 #include <limits.h>
25 #include <pthread.h>
26 #include <signal.h>
27 #include <stdbool.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <time.h>
31 #include <unistd.h>
32 #include <sys/param.h>
33 #include <sys/types.h>
34 
35 #ifndef MAX_THREADS
36 # define MAX_THREADS		100000
37 #endif
38 #ifndef DEFAULT_THREADS
39 # define DEFAULT_THREADS	50
40 #endif
41 
42 
43 #define OPT_TO_THREAD		300
44 #define OPT_TO_PROCESS		301
45 #define OPT_SYNC_SIGNAL		302
46 #define OPT_SYNC_JOIN		303
47 #define OPT_TOPLEVEL		304
48 
49 
50 static const struct argp_option options[] =
51   {
52     { NULL, 0, NULL, 0, "\
53 This is a test for threads so we allow ther user to selection the number of \
54 threads which are used at any one time.  Independently the total number of \
55 rounds can be selected.  This is the total number of threads which will have \
56 run when the process terminates:" },
57     { "threads", 't', "NUMBER", 0, "Number of threads used at once" },
58     { "starts", 's', "NUMBER", 0, "Total number of working threads" },
59     { "toplevel", OPT_TOPLEVEL, "NUMBER", 0,
60       "Number of toplevel threads which start the other threads; this \
61 implies --sync-join" },
62 
63     { NULL, 0, NULL, 0, "\
64 Each thread can do one of two things: sleep or do work.  The latter is 100% \
65 CPU bound.  The work load is the probability a thread does work.  All values \
66 from zero to 100 (inclusive) are valid.  How often each thread repeats this \
67 can be determined by the number of rounds.  The work cost determines how long \
68 each work session (not sleeping) takes.  If it is zero a thread would \
69 effectively nothing.  By setting the number of rounds to zero the thread \
70 does no work at all and pure thread creation times can be measured." },
71     { "workload", 'w', "PERCENT", 0, "Percentage of time spent working" },
72     { "workcost", 'c', "NUMBER", 0,
73       "Factor in the cost of each round of working" },
74     { "rounds", 'r', "NUMBER", 0, "Number of rounds each thread runs" },
75 
76     { NULL, 0, NULL, 0, "\
77 There are a number of different methods how thread creation can be \
78 synchronized.  Synchronization is necessary since the number of concurrently \
79 running threads is limited." },
80     { "sync-signal", OPT_SYNC_SIGNAL, NULL, 0,
81       "Synchronize using a signal (default)" },
82     { "sync-join", OPT_SYNC_JOIN, NULL, 0, "Synchronize using pthread_join" },
83 
84     { NULL, 0, NULL, 0, "\
85 One parameter for each threads execution is the size of the stack.  If this \
86 parameter is not used the system's default stack size is used.  If many \
87 threads are used the stack size should be chosen quite small." },
88     { "stacksize", 'S', "BYTES", 0, "Size of threads stack" },
89     { "guardsize", 'g', "BYTES", 0,
90       "Size of stack guard area; must fit into the stack" },
91 
92     { NULL, 0, NULL, 0, "Signal options:" },
93     { "to-thread", OPT_TO_THREAD, NULL, 0, "Send signal to main thread" },
94     { "to-process", OPT_TO_PROCESS, NULL, 0,
95       "Send signal to process (default)" },
96 
97     { NULL, 0, NULL, 0, "Administrative options:" },
98     { "progress", 'p', NULL, 0, "Show signs of progress" },
99     { "timing", 'T', NULL, 0,
100       "Measure time from startup to the last thread finishing" },
101     { NULL, 0, NULL, 0, NULL }
102   };
103 
104 /* Prototype for option handler.  */
105 static error_t parse_opt (int key, char *arg, struct argp_state *state);
106 
107 /* Data structure to communicate with argp functions.  */
108 static struct argp argp =
109 {
110   options, parse_opt
111 };
112 
113 
114 static unsigned long int threads = DEFAULT_THREADS;
115 static unsigned long int workload = 75;
116 static unsigned long int workcost = 20;
117 static unsigned long int rounds = 10;
118 static long int starts = 5000;
119 static unsigned long int stacksize;
120 static long int guardsize = -1;
121 static bool progress;
122 static bool timing;
123 static bool to_thread;
124 static unsigned long int toplevel = 1;
125 
126 
127 static long int running;
128 static pthread_mutex_t running_mutex = PTHREAD_MUTEX_INITIALIZER;
129 
130 static pid_t pid;
131 static pthread_t tmain;
132 
133 static clockid_t cl;
134 static struct timespec start_time;
135 
136 
137 static pthread_mutex_t sum_mutex = PTHREAD_MUTEX_INITIALIZER;
138 unsigned int sum;
139 
140 static enum
141   {
142     sync_signal,
143     sync_join
144   }
145 sync_method;
146 
147 
148 /* We use 64bit values for the times.  */
149 typedef unsigned long long int hp_timing_t;
150 
151 
152 /* Attributes for all created threads.  */
153 static pthread_attr_t attr;
154 
155 
156 static void *
work(void * arg)157 work (void *arg)
158 {
159   unsigned long int i;
160   unsigned int state = (unsigned long int) arg;
161 
162   for (i = 0; i < rounds; ++i)
163     {
164       /* Determine what to do.  */
165       unsigned int rnum;
166 
167       /* Uniform distribution.  */
168       do
169 	rnum = rand_r (&state);
170       while (rnum >= UINT_MAX - (UINT_MAX % 100));
171 
172       rnum %= 100;
173 
174       if (rnum < workload)
175 	{
176 	  int j;
177 	  int a[4] = { i, rnum, i + rnum, rnum - i };
178 
179 	  if (progress)
180 	    write (STDERR_FILENO, "c", 1);
181 
182 	  for (j = 0; j < workcost; ++j)
183 	    {
184 	      a[0] += a[3] >> 12;
185 	      a[1] += a[2] >> 20;
186 	      a[2] += a[1] ^ 0x3423423;
187 	      a[3] += a[0] - a[1];
188 	    }
189 
190 	  pthread_mutex_lock (&sum_mutex);
191 	  sum += a[0] + a[1] + a[2] + a[3];
192 	  pthread_mutex_unlock (&sum_mutex);
193 	}
194       else
195 	{
196 	  /* Just sleep.  */
197 	  struct timespec tv;
198 
199 	  tv.tv_sec = 0;
200 	  tv.tv_nsec = 10000000;
201 
202 	  if (progress)
203 	    write (STDERR_FILENO, "w", 1);
204 
205 	  nanosleep (&tv, NULL);
206 	}
207     }
208 
209   return NULL;
210 }
211 
212 
213 static void *
thread_function(void * arg)214 thread_function (void *arg)
215 {
216   work (arg);
217 
218   pthread_mutex_lock (&running_mutex);
219   if (--running <= 0 && starts <= 0)
220     {
221       /* We are done.  */
222       if (progress)
223 	write (STDERR_FILENO, "\n", 1);
224 
225       if (timing)
226 	{
227 	  struct timespec end_time;
228 
229 	  if (clock_gettime (cl, &end_time) == 0)
230 	    {
231 	      end_time.tv_sec -= start_time.tv_sec;
232 	      end_time.tv_nsec -= start_time.tv_nsec;
233 	      if (end_time.tv_nsec < 0)
234 		{
235 		  end_time.tv_nsec += 1000000000;
236 		  --end_time.tv_sec;
237 		}
238 
239 	      printf ("\nRuntime: %lu.%09lu seconds\n",
240 		      (unsigned long int) end_time.tv_sec,
241 		      (unsigned long int) end_time.tv_nsec);
242 	    }
243 	}
244 
245       printf ("Result: %08x\n", sum);
246 
247       exit (0);
248     }
249   pthread_mutex_unlock (&running_mutex);
250 
251   if (sync_method == sync_signal)
252     {
253       if (to_thread)
254 	/* This code sends a signal to the main thread.  */
255 	pthread_kill (tmain, SIGUSR1);
256       else
257 	/* Use this code to test sending a signal to the process.  */
258 	kill (pid, SIGUSR1);
259     }
260 
261   if (progress)
262     write (STDERR_FILENO, "f", 1);
263 
264   return NULL;
265 }
266 
267 
268 struct start_info
269 {
270   unsigned int starts;
271   unsigned int threads;
272 };
273 
274 
275 static void *
start_threads(void * arg)276 start_threads (void *arg)
277 {
278   struct start_info *si = arg;
279   unsigned int starts = si->starts;
280   pthread_t ths[si->threads];
281   unsigned int state = starts;
282   unsigned int n;
283   unsigned int i = 0;
284   int err;
285 
286   if (progress)
287     write (STDERR_FILENO, "T", 1);
288 
289   memset (ths, '\0', sizeof (pthread_t) * si->threads);
290 
291   while (starts-- > 0)
292     {
293       if (ths[i] != 0)
294 	{
295 	  /* Wait for the threads in the order they were created.  */
296 	  err = pthread_join (ths[i], NULL);
297 	  if (err != 0)
298 	    error (EXIT_FAILURE, err, "cannot join thread");
299 
300 	  if (progress)
301 	    write (STDERR_FILENO, "f", 1);
302 	}
303 
304       err = pthread_create (&ths[i], &attr, work,
305 			    (void *) (long) (rand_r (&state) + starts + i));
306 
307       if (err != 0)
308 	error (EXIT_FAILURE, err, "cannot start thread");
309 
310       if (progress)
311 	write (STDERR_FILENO, "t", 1);
312 
313       if (++i == si->threads)
314 	i = 0;
315     }
316 
317   n = i;
318   do
319     {
320       if (ths[i] != 0)
321 	{
322 	  err = pthread_join (ths[i], NULL);
323 	  if (err != 0)
324 	    error (EXIT_FAILURE, err, "cannot join thread");
325 
326 	  if (progress)
327 	    write (STDERR_FILENO, "f", 1);
328 	}
329 
330       if (++i == si->threads)
331 	i = 0;
332     }
333   while (i != n);
334 
335   if (progress)
336     write (STDERR_FILENO, "F", 1);
337 
338   return NULL;
339 }
340 
341 
342 int
main(int argc,char * argv[])343 main (int argc, char *argv[])
344 {
345   int remaining;
346   sigset_t ss;
347   pthread_t th;
348   pthread_t *ths = NULL;
349   int empty = 0;
350   int last;
351   bool cont = true;
352 
353   /* Parse and process arguments.  */
354   argp_parse (&argp, argc, argv, 0, &remaining, NULL);
355 
356   if (sync_method == sync_join)
357     {
358       ths = (pthread_t *) calloc (threads, sizeof (pthread_t));
359       if (ths == NULL)
360 	error (EXIT_FAILURE, errno,
361 	       "cannot allocate memory for thread descriptor array");
362 
363       last = threads;
364     }
365   else
366     {
367       ths = &th;
368       last = 1;
369     }
370 
371   if (toplevel > threads)
372     {
373       printf ("resetting number of toplevel threads to %lu to not surpass number to concurrent threads\n",
374 	      threads);
375       toplevel = threads;
376     }
377 
378   if (timing)
379     {
380       if (clock_getcpuclockid (0, &cl) != 0
381 	  || clock_gettime (cl, &start_time) != 0)
382 	timing = false;
383     }
384 
385   /* We need this later.  */
386   pid = getpid ();
387   tmain = pthread_self ();
388 
389   /* We use signal SIGUSR1 for communication between the threads and
390      the main thread.  We only want sychronous notification.  */
391   if (sync_method == sync_signal)
392     {
393       sigemptyset (&ss);
394       sigaddset (&ss, SIGUSR1);
395       if (sigprocmask (SIG_BLOCK, &ss, NULL) != 0)
396 	error (EXIT_FAILURE, errno, "cannot set signal mask");
397     }
398 
399   /* Create the thread attributes.  */
400   pthread_attr_init (&attr);
401 
402   /* If the user provided a stack size use it.  */
403   if (stacksize != 0
404       && pthread_attr_setstacksize (&attr, stacksize) != 0)
405     puts ("could not set stack size; will use default");
406   /* And stack guard size.  */
407   if (guardsize != -1
408       && pthread_attr_setguardsize (&attr, guardsize) != 0)
409     puts ("invalid stack guard size; will use default");
410 
411   /* All threads are created detached if we are not using pthread_join
412      to synchronize.  */
413   if (sync_method != sync_join)
414     pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
415 
416   if (sync_method == sync_signal)
417     {
418       while (1)
419 	{
420 	  int err;
421 	  bool do_wait = false;
422 
423 	  pthread_mutex_lock (&running_mutex);
424 	  if (starts-- < 0)
425 	    cont = false;
426 	  else
427 	    do_wait = ++running >= threads && starts > 0;
428 
429 	  pthread_mutex_unlock (&running_mutex);
430 
431 	  if (! cont)
432 	    break;
433 
434 	  if (progress)
435 	    write (STDERR_FILENO, "t", 1);
436 
437 	  err = pthread_create (&ths[empty], &attr, thread_function,
438 				(void *) starts);
439 	  if (err != 0)
440 	    error (EXIT_FAILURE, err, "cannot start thread %lu", starts);
441 
442 	  if (++empty == last)
443 	    empty = 0;
444 
445 	  if (do_wait)
446 	    sigwaitinfo (&ss, NULL);
447 	}
448 
449       /* Do nothing anymore.  On of the threads will terminate the program.  */
450       sigfillset (&ss);
451       sigdelset (&ss, SIGINT);
452       while (1)
453 	sigsuspend (&ss);
454     }
455   else
456     {
457       pthread_t ths[toplevel];
458       struct start_info si[toplevel];
459       unsigned int i;
460 
461       for (i = 0; i < toplevel; ++i)
462 	{
463 	  unsigned int child_starts = starts / (toplevel - i);
464 	  unsigned int child_threads = threads / (toplevel - i);
465 	  int err;
466 
467 	  si[i].starts = child_starts;
468 	  si[i].threads = child_threads;
469 
470 	  err = pthread_create (&ths[i], &attr, start_threads, &si[i]);
471 	  if (err != 0)
472 	    error (EXIT_FAILURE, err, "cannot start thread");
473 
474 	  starts -= child_starts;
475 	  threads -= child_threads;
476 	}
477 
478       for (i = 0; i < toplevel; ++i)
479 	{
480 	  int err = pthread_join (ths[i], NULL);
481 
482 	  if (err != 0)
483 	    error (EXIT_FAILURE, err, "cannot join thread");
484 	}
485 
486       /* We are done.  */
487       if (progress)
488 	write (STDERR_FILENO, "\n", 1);
489 
490       if (timing)
491 	{
492 	  struct timespec end_time;
493 
494 	  if (clock_gettime (cl, &end_time) == 0)
495 	    {
496 	      end_time.tv_sec -= start_time.tv_sec;
497 	      end_time.tv_nsec -= start_time.tv_nsec;
498 	      if (end_time.tv_nsec < 0)
499 		{
500 		  end_time.tv_nsec += 1000000000;
501 		  --end_time.tv_sec;
502 		}
503 
504 	      printf ("\nRuntime: %lu.%09lu seconds\n",
505 		      (unsigned long int) end_time.tv_sec,
506 		      (unsigned long int) end_time.tv_nsec);
507 	    }
508 	}
509 
510       printf ("Result: %08x\n", sum);
511 
512       exit (0);
513     }
514 
515   /* NOTREACHED */
516   return 0;
517 }
518 
519 
520 /* Handle program arguments.  */
521 static error_t
parse_opt(int key,char * arg,struct argp_state * state)522 parse_opt (int key, char *arg, struct argp_state *state)
523 {
524   unsigned long int num;
525   long int snum;
526 
527   switch (key)
528     {
529     case 't':
530       num = strtoul (arg, NULL, 0);
531       if (num <= MAX_THREADS)
532 	threads = num;
533       else
534 	printf ("\
535 number of threads limited to %u; recompile with a higher limit if necessary",
536 		MAX_THREADS);
537       break;
538 
539     case 'w':
540       num = strtoul (arg, NULL, 0);
541       if (num <= 100)
542 	workload = num;
543       else
544 	puts ("workload must be between 0 and 100 percent");
545       break;
546 
547     case 'c':
548       workcost = strtoul (arg, NULL, 0);
549       break;
550 
551     case 'r':
552       rounds = strtoul (arg, NULL, 0);
553       break;
554 
555     case 's':
556       starts = strtoul (arg, NULL, 0);
557       break;
558 
559     case 'S':
560       num = strtoul (arg, NULL, 0);
561       if (num >= PTHREAD_STACK_MIN)
562 	stacksize = num;
563       else
564 	printf ("minimum stack size is %d\n", PTHREAD_STACK_MIN);
565       break;
566 
567     case 'g':
568       snum = strtol (arg, NULL, 0);
569       if (snum < 0)
570 	printf ("invalid guard size %s\n", arg);
571       else
572 	guardsize = snum;
573       break;
574 
575     case 'p':
576       progress = true;
577       break;
578 
579     case 'T':
580       timing = true;
581       break;
582 
583     case OPT_TO_THREAD:
584       to_thread = true;
585       break;
586 
587     case OPT_TO_PROCESS:
588       to_thread = false;
589       break;
590 
591     case OPT_SYNC_SIGNAL:
592       sync_method = sync_signal;
593       break;
594 
595     case OPT_SYNC_JOIN:
596       sync_method = sync_join;
597       break;
598 
599     case OPT_TOPLEVEL:
600       num = strtoul (arg, NULL, 0);
601       if (num < MAX_THREADS)
602 	toplevel = num;
603       else
604 	printf ("\
605 number of threads limited to %u; recompile with a higher limit if necessary",
606 		MAX_THREADS);
607       sync_method = sync_join;
608       break;
609 
610     default:
611       return ARGP_ERR_UNKNOWN;
612     }
613 
614   return 0;
615 }
616 
617 
618 static hp_timing_t
get_clockfreq(void)619 get_clockfreq (void)
620 {
621   /* We read the information from the /proc filesystem.  It contains at
622      least one line like
623 	cpu MHz         : 497.840237
624      or also
625 	cpu MHz         : 497.841
626      We search for this line and convert the number in an integer.  */
627   static hp_timing_t result;
628   int fd;
629 
630   /* If this function was called before, we know the result.  */
631   if (result != 0)
632     return result;
633 
634   fd = open ("/proc/cpuinfo", O_RDONLY);
635   if (__glibc_likely (fd != -1))
636     {
637       /* XXX AFAIK the /proc filesystem can generate "files" only up
638          to a size of 4096 bytes.  */
639       char buf[4096];
640       ssize_t n;
641 
642       n = read (fd, buf, sizeof buf);
643       if (__builtin_expect (n, 1) > 0)
644 	{
645 	  char *mhz = memmem (buf, n, "cpu MHz", 7);
646 
647 	  if (__glibc_likely (mhz != NULL))
648 	    {
649 	      char *endp = buf + n;
650 	      int seen_decpoint = 0;
651 	      int ndigits = 0;
652 
653 	      /* Search for the beginning of the string.  */
654 	      while (mhz < endp && (*mhz < '0' || *mhz > '9') && *mhz != '\n')
655 		++mhz;
656 
657 	      while (mhz < endp && *mhz != '\n')
658 		{
659 		  if (*mhz >= '0' && *mhz <= '9')
660 		    {
661 		      result *= 10;
662 		      result += *mhz - '0';
663 		      if (seen_decpoint)
664 			++ndigits;
665 		    }
666 		  else if (*mhz == '.')
667 		    seen_decpoint = 1;
668 
669 		  ++mhz;
670 		}
671 
672 	      /* Compensate for missing digits at the end.  */
673 	      while (ndigits++ < 6)
674 		result *= 10;
675 	    }
676 	}
677 
678       close (fd);
679     }
680 
681   return result;
682 }
683 
684 
685 int
clock_getcpuclockid(pid_t pid,clockid_t * clock_id)686 clock_getcpuclockid (pid_t pid, clockid_t *clock_id)
687 {
688   /* We don't allow any process ID but our own.  */
689   if (pid != 0 && pid != getpid ())
690     return EPERM;
691 
692 #ifdef CLOCK_PROCESS_CPUTIME_ID
693   /* Store the number.  */
694   *clock_id = CLOCK_PROCESS_CPUTIME_ID;
695 
696   return 0;
697 #else
698   /* We don't have a timer for that.  */
699   return ENOENT;
700 #endif
701 }
702 
703 
704 #ifdef i386
705 #define HP_TIMING_NOW(Var)	__asm__ __volatile__ ("rdtsc" : "=A" (Var))
706 #elif defined __x86_64__
707 # define HP_TIMING_NOW(Var) \
708   ({ unsigned int _hi, _lo; \
709      asm volatile ("rdtsc" : "=a" (_lo), "=d" (_hi)); \
710      (Var) = ((unsigned long long int) _hi << 32) | _lo; })
711 #elif defined __ia64__
712 #define HP_TIMING_NOW(Var)	__asm__ __volatile__ ("mov %0=ar.itc" : "=r" (Var) : : "memory")
713 #else
714 #error "HP_TIMING_NOW missing"
715 #endif
716 
717 /* Get current value of CLOCK and store it in TP.  */
718 int
clock_gettime(clockid_t clock_id,struct timespec * tp)719 clock_gettime (clockid_t clock_id, struct timespec *tp)
720 {
721   int retval = -1;
722 
723   switch (clock_id)
724     {
725     case CLOCK_PROCESS_CPUTIME_ID:
726       {
727 
728 	static hp_timing_t freq;
729 	hp_timing_t tsc;
730 
731 	/* Get the current counter.  */
732 	HP_TIMING_NOW (tsc);
733 
734 	if (freq == 0)
735 	  {
736 	    freq = get_clockfreq ();
737 	    if (freq == 0)
738 	      return EINVAL;
739 	  }
740 
741 	/* Compute the seconds.  */
742 	tp->tv_sec = tsc / freq;
743 
744 	/* And the nanoseconds.  This computation should be stable until
745 	   we get machines with about 16GHz frequency.  */
746 	tp->tv_nsec = ((tsc % freq) * UINT64_C (1000000000)) / freq;
747 
748 	retval = 0;
749       }
750     break;
751 
752     default:
753       errno = EINVAL;
754       break;
755     }
756 
757   return retval;
758 }
759