xref: /openbsd-src/regress/sys/kern/signal/sigprof/sigprof.c (revision 5c7cf20602316143c9f023de9b317319b1318a62)
1 /*	$OpenBSD: sigprof.c,v 1.2 2021/07/06 13:19:57 bluhm Exp $ */
2 /*
3  * Copyright (c) 2013 Joel Sing <jsing@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 /*
19  * Test that profiling signals are delivered to the thread whose execution
20  * consumed the CPU time and resulted in the profiling timer expiring.
21  * Inspired by a problem report test case from Russ Cox <rsc@golang.org>.
22  */
23 
24 #include <err.h>
25 #include <pthread.h>
26 #include <signal.h>
27 #include <stdio.h>
28 #include <string.h>
29 
30 #define NTHREADS	4
31 #define NSIGNALS	100
32 #define NSIGTOTAL	(NTHREADS * NSIGNALS)
33 #define NMINSIG		((NSIGNALS * 50) / 100)
34 
35 void handler(int);
36 void *spinloop(void *);
37 
38 pthread_t threads[NTHREADS + 1];
39 int sigcount[NTHREADS + 1];
40 volatile int sigtotal;
41 volatile int done;
42 
43 void
handler(int sig)44 handler(int sig)
45 {
46 	pthread_t self;
47 	int i;
48 
49 	/*
50 	 * pthread_self() is not required to be async-signal-safe, however
51 	 * the OpenBSD implementation currently is.
52 	 */
53 	self = pthread_self();
54 
55 	for (i = 0; i <= NTHREADS; i++)
56 		if (threads[i] == self)
57 			sigcount[i]++;
58 
59 	if (++sigtotal >= NSIGTOTAL)
60 		done = 1;
61 }
62 
63 void *
spinloop(void * arg)64 spinloop(void *arg)
65 {
66 	while (!done)
67 		;
68 
69 	pthread_exit(NULL);
70 }
71 
72 int
main(int argc,char ** argv)73 main(int argc, char **argv)
74 {
75 	struct sigaction sa;
76 	struct itimerval it;
77 	int i;
78 
79 	bzero(&sa, sizeof(sa));
80 	sa.sa_handler = handler;
81 	sa.sa_flags = SA_RESTART;
82 	sigfillset(&sa.sa_mask);
83 	sigaction(SIGPROF, &sa, 0);
84 
85 	threads[0] = pthread_self();
86 	for (i = 1; i <= NTHREADS; i++)
87 		if (pthread_create(&threads[i], NULL, spinloop, NULL) != 0)
88 			err(1, "pthread_create");
89 
90 	bzero(&it, sizeof(it));
91 	it.it_interval.tv_usec = 10000;
92 	it.it_value = it.it_interval;
93 	setitimer(ITIMER_PROF, &it, NULL);
94 
95 	for (i = 1; i <= NTHREADS; i++)
96 		if (pthread_join(threads[i], NULL) != 0)
97 			err(1, "pthread_join");
98 
99 	bzero(&it, sizeof(it));
100 	setitimer(ITIMER_PROF, &it, NULL);
101 
102 	fprintf(stderr, "total profiling signals: %d\n", sigtotal);
103 	fprintf(stderr, "minimum signals per thread: %d\n", NMINSIG);
104 	fprintf(stderr, "main thread - %d\n", sigcount[0]);
105 	for (i = 1; i <= NTHREADS; i++)
106 		fprintf(stderr, "thread %d - %d\n", i, sigcount[i]);
107 
108 	if (sigtotal < NSIGTOTAL)
109 		errx(1, "insufficient profiling signals (%d < %d)",
110 			sigtotal, NSIGTOTAL);
111 
112 	/*
113 	 * The main thread is effectively sleeping and should have received
114 	 * almost no profiling signals. Allow a small tolerance.
115 	 */
116 	if (sigcount[0] > ((NSIGNALS * 10) / 100))
117 		errx(1, "main thread received too many signals (%d)",
118 			sigcount[0]);
119 
120 	/*
121 	 * Ensure that the kernel delivered the profiling signals to the
122 	 * thread that consumed the CPU time. In an ideal world each thread
123 	 * would have received equal CPU time and an equal number of signals.
124 	 * In the less than ideal world we'll just settle for a percentage.
125 	 */
126 	for (i = 1; i <= NTHREADS; i++)
127 		if (sigcount[i] < NMINSIG)
128 			errx(1, "thread %d received only %d signals (%d < %d)",
129 				i, sigcount[i], sigcount[i], NMINSIG);
130 
131 	return 0;
132 }
133