xref: /openbsd-src/regress/sys/kern/signal/sigprof/sigprof.c (revision 5c7cf20602316143c9f023de9b317319b1318a62)
1*5c7cf206Sbluhm /*	$OpenBSD: sigprof.c,v 1.2 2021/07/06 13:19:57 bluhm Exp $ */
26bcfccdaSmpi /*
36bcfccdaSmpi  * Copyright (c) 2013 Joel Sing <jsing@openbsd.org>
46bcfccdaSmpi  *
56bcfccdaSmpi  * Permission to use, copy, modify, and distribute this software for any
66bcfccdaSmpi  * purpose with or without fee is hereby granted, provided that the above
76bcfccdaSmpi  * copyright notice and this permission notice appear in all copies.
86bcfccdaSmpi  *
96bcfccdaSmpi  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
106bcfccdaSmpi  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
116bcfccdaSmpi  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
126bcfccdaSmpi  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
136bcfccdaSmpi  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
146bcfccdaSmpi  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
156bcfccdaSmpi  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
166bcfccdaSmpi  */
176bcfccdaSmpi 
186bcfccdaSmpi /*
196bcfccdaSmpi  * Test that profiling signals are delivered to the thread whose execution
206bcfccdaSmpi  * consumed the CPU time and resulted in the profiling timer expiring.
216bcfccdaSmpi  * Inspired by a problem report test case from Russ Cox <rsc@golang.org>.
226bcfccdaSmpi  */
236bcfccdaSmpi 
246bcfccdaSmpi #include <err.h>
256bcfccdaSmpi #include <pthread.h>
266bcfccdaSmpi #include <signal.h>
276bcfccdaSmpi #include <stdio.h>
286bcfccdaSmpi #include <string.h>
296bcfccdaSmpi 
306bcfccdaSmpi #define NTHREADS	4
316bcfccdaSmpi #define NSIGNALS	100
326bcfccdaSmpi #define NSIGTOTAL	(NTHREADS * NSIGNALS)
33*5c7cf206Sbluhm #define NMINSIG		((NSIGNALS * 50) / 100)
346bcfccdaSmpi 
356bcfccdaSmpi void handler(int);
366bcfccdaSmpi void *spinloop(void *);
376bcfccdaSmpi 
386bcfccdaSmpi pthread_t threads[NTHREADS + 1];
396bcfccdaSmpi int sigcount[NTHREADS + 1];
406bcfccdaSmpi volatile int sigtotal;
416bcfccdaSmpi volatile int done;
426bcfccdaSmpi 
436bcfccdaSmpi void
handler(int sig)446bcfccdaSmpi handler(int sig)
456bcfccdaSmpi {
466bcfccdaSmpi 	pthread_t self;
476bcfccdaSmpi 	int i;
486bcfccdaSmpi 
496bcfccdaSmpi 	/*
506bcfccdaSmpi 	 * pthread_self() is not required to be async-signal-safe, however
516bcfccdaSmpi 	 * the OpenBSD implementation currently is.
526bcfccdaSmpi 	 */
536bcfccdaSmpi 	self = pthread_self();
546bcfccdaSmpi 
556bcfccdaSmpi 	for (i = 0; i <= NTHREADS; i++)
566bcfccdaSmpi 		if (threads[i] == self)
576bcfccdaSmpi 			sigcount[i]++;
586bcfccdaSmpi 
596bcfccdaSmpi 	if (++sigtotal >= NSIGTOTAL)
606bcfccdaSmpi 		done = 1;
616bcfccdaSmpi }
626bcfccdaSmpi 
636bcfccdaSmpi void *
spinloop(void * arg)646bcfccdaSmpi spinloop(void *arg)
656bcfccdaSmpi {
666bcfccdaSmpi 	while (!done)
676bcfccdaSmpi 		;
686bcfccdaSmpi 
696bcfccdaSmpi 	pthread_exit(NULL);
706bcfccdaSmpi }
716bcfccdaSmpi 
726bcfccdaSmpi int
main(int argc,char ** argv)736bcfccdaSmpi main(int argc, char **argv)
746bcfccdaSmpi {
756bcfccdaSmpi 	struct sigaction sa;
766bcfccdaSmpi 	struct itimerval it;
776bcfccdaSmpi 	int i;
786bcfccdaSmpi 
796bcfccdaSmpi 	bzero(&sa, sizeof(sa));
806bcfccdaSmpi 	sa.sa_handler = handler;
816bcfccdaSmpi 	sa.sa_flags = SA_RESTART;
826bcfccdaSmpi 	sigfillset(&sa.sa_mask);
836bcfccdaSmpi 	sigaction(SIGPROF, &sa, 0);
846bcfccdaSmpi 
856bcfccdaSmpi 	threads[0] = pthread_self();
866bcfccdaSmpi 	for (i = 1; i <= NTHREADS; i++)
876bcfccdaSmpi 		if (pthread_create(&threads[i], NULL, spinloop, NULL) != 0)
886bcfccdaSmpi 			err(1, "pthread_create");
896bcfccdaSmpi 
906bcfccdaSmpi 	bzero(&it, sizeof(it));
916bcfccdaSmpi 	it.it_interval.tv_usec = 10000;
926bcfccdaSmpi 	it.it_value = it.it_interval;
936bcfccdaSmpi 	setitimer(ITIMER_PROF, &it, NULL);
946bcfccdaSmpi 
956bcfccdaSmpi 	for (i = 1; i <= NTHREADS; i++)
966bcfccdaSmpi 		if (pthread_join(threads[i], NULL) != 0)
976bcfccdaSmpi 			err(1, "pthread_join");
986bcfccdaSmpi 
996bcfccdaSmpi 	bzero(&it, sizeof(it));
1006bcfccdaSmpi 	setitimer(ITIMER_PROF, &it, NULL);
1016bcfccdaSmpi 
1026bcfccdaSmpi 	fprintf(stderr, "total profiling signals: %d\n", sigtotal);
1036bcfccdaSmpi 	fprintf(stderr, "minimum signals per thread: %d\n", NMINSIG);
1046bcfccdaSmpi 	fprintf(stderr, "main thread - %d\n", sigcount[0]);
1056bcfccdaSmpi 	for (i = 1; i <= NTHREADS; i++)
1066bcfccdaSmpi 		fprintf(stderr, "thread %d - %d\n", i, sigcount[i]);
1076bcfccdaSmpi 
1086bcfccdaSmpi 	if (sigtotal < NSIGTOTAL)
1096bcfccdaSmpi 		errx(1, "insufficient profiling signals (%d < %d)",
1106bcfccdaSmpi 			sigtotal, NSIGTOTAL);
1116bcfccdaSmpi 
1126bcfccdaSmpi 	/*
1136bcfccdaSmpi 	 * The main thread is effectively sleeping and should have received
1146bcfccdaSmpi 	 * almost no profiling signals. Allow a small tolerance.
1156bcfccdaSmpi 	 */
1166bcfccdaSmpi 	if (sigcount[0] > ((NSIGNALS * 10) / 100))
1176bcfccdaSmpi 		errx(1, "main thread received too many signals (%d)",
1186bcfccdaSmpi 			sigcount[0]);
1196bcfccdaSmpi 
1206bcfccdaSmpi 	/*
1216bcfccdaSmpi 	 * Ensure that the kernel delivered the profiling signals to the
1226bcfccdaSmpi 	 * thread that consumed the CPU time. In an ideal world each thread
1236bcfccdaSmpi 	 * would have received equal CPU time and an equal number of signals.
1246bcfccdaSmpi 	 * In the less than ideal world we'll just settle for a percentage.
1256bcfccdaSmpi 	 */
1266bcfccdaSmpi 	for (i = 1; i <= NTHREADS; i++)
1276bcfccdaSmpi 		if (sigcount[i] < NMINSIG)
1286bcfccdaSmpi 			errx(1, "thread %d received only %d signals (%d < %d)",
1296bcfccdaSmpi 				i, sigcount[i], sigcount[i], NMINSIG);
1306bcfccdaSmpi 
1316bcfccdaSmpi 	return 0;
1326bcfccdaSmpi }
133