1 /* $OpenBSD: sigprof.c,v 1.1 2020/09/16 14:02:24 mpi 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 * 75) / 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 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 * 64 spinloop(void *arg) 65 { 66 while (!done) 67 ; 68 69 pthread_exit(NULL); 70 } 71 72 int 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