From 38593e942ad4b16b2c3c43ea521167368771cbfb Mon Sep 17 00:00:00 2001 From: "Dmitry V. Levin" Date: Wed, 26 Feb 2014 16:51:28 +0000 Subject: Rewrite signal mask decoding without sigset_t The sigset_t provided by libc is not quite convenient. In glibc, sigset_t is an array with space for 1024 bits, which is much more than required: all architectures supported by Linux have only 64 signals except MIPS, which has 128. In bionic libc, LP32 sigset_t is only 4 bytes long, which is less than necessary. With this change, signal mask is decoded without use of intermediate sigset_t structure, which saves us some cpu cycles in case of glibc with its inflated sigset_t, and enables build with libcs where sigset_t is broken. Old implementation used to check each signal number in the given signal mask twice using sigismember(). New implementation is based on popcount and next_set_bit() so it's noticeably faster. * configure.ac: Check for __builtin_popcount. * signal.c: Ensure that NSIG >= 32. (sprintsigmask, sprintsigmask_long, printsigmask): Remove. (popcount32, sprintsigmask_n): New functions. (tprintsigmask_addr, sprintsigmask_val, tprintsigmask_val): New macros. (print_sigset_addr_len, sys_sigsetmask, sys_sigreturn, sys_siggetmask, sys_sigsuspend, sys_sigprocmask, decode_new_sigaction): Update to use new signal mask decoding interface. * tests/sigaction.c (main): Add a test with almost filled signal mask. * tests/sigaction.awk: Update. --- configure.ac | 9 +++ signal.c | 206 +++++++++++++++++++++++++--------------------------- tests/sigaction.awk | 22 +++++- tests/sigaction.c | 12 ++- 4 files changed, 134 insertions(+), 115 deletions(-) diff --git a/configure.ac b/configure.ac index 75eafc8..caa5aed 100644 --- a/configure.ac +++ b/configure.ac @@ -309,6 +309,15 @@ if test "x$st_cv_sa_restorer" != xno; then [SA_RESTORER defined in ]) fi +AC_CACHE_CHECK([for __builtin_popcount], [st_cv_have___builtin_popcount], + [AC_LINK_IFELSE([AC_LANG_PROGRAM([], [__builtin_popcount(0)])], + [st_cv_have___builtin_popcount=yes], + [st_cv_have___builtin_popcount=no])]) +if test "x$st_cv_have___builtin_popcount" = xyes; then + AC_DEFINE([HAVE___BUILTIN_POPCOUNT], [1], + [Define to 1 if the system provides __builtin_popcount function]) +fi + AC_PATH_PROG([PERL], [perl]) AC_CONFIG_FILES([Makefile tests/Makefile]) diff --git a/signal.c b/signal.c index e2c929f..a13f7ac 100644 --- a/signal.c +++ b/signal.c @@ -86,6 +86,8 @@ struct sigcontext { #ifndef NSIG # warning: NSIG is not defined, using 32 # define NSIG 32 +#elif NSIG < 32 +# error: NSIG < 32 #endif #ifdef HAVE_SIGACTION @@ -211,64 +213,75 @@ signame(int sig) return buf; } -static const char * -sprintsigmask(const char *str, sigset_t *mask) -/* set might include realtime sigs */ +static unsigned int +popcount32(const uint32_t *a, unsigned int size) { - /* Was [8 * sizeof(sigset_t) * 8], but - * glibc sigset_t is huge (1024 bits = 128 *bytes*), - * and we were ending up with 8k (!) buffer here. - * - * No Unix system can have sig > 255 - * (waitpid API won't be able to indicate death from one) - * and sig 0 doesn't exist either. - * Therefore max possible no of sigs is 255: 1..255 - */ - static char outstr[8 * (255 * 2 / 3)]; + unsigned int count = 0; - int i, nsigs; - int maxsigs; - int show_members; - char sep; - char *s; + for (; size; ++a, --size) { + uint32_t x = *a; - /* Note: nsignals = ARRAY_SIZE(signalent[]), - * and that array may not have SIGRTnn. - */ -#ifdef __SIGRTMAX - maxsigs = __SIGRTMAX + 1; /* instead */ +#ifdef HAVE___BUILTIN_POPCOUNT + count += __builtin_popcount(x); #else - maxsigs = nsignals; + for (; x; ++count) + x &= x - 1; #endif - s = stpcpy(outstr, str); - nsigs = 0; - for (i = 1; i < maxsigs; i++) { - if (sigismember(mask, i) == 1) - nsigs++; } - /* 1: show mask members, 0: show those which are NOT in mask */ - show_members = (nsigs < nsignals * 2 / 3); - if (!show_members) + return count; +} + +static const char * +sprintsigmask_n(const char *prefix, const void *sig_mask, unsigned int bytes) +{ + /* + * The maximum number of signal names to be printed is NSIG * 2 / 3. + * Most of signal names have length 7, + * average length of signal names is less than 7. + * The length of prefix string does not exceed 16. + */ + static char outstr[128 + 8 * (NSIG * 2 / 3)]; + + char *s; + const uint32_t *mask; + uint32_t inverted_mask[NSIG / 32]; + unsigned int size; + int i; + char sep; + + s = stpcpy(outstr, prefix); + + mask = sig_mask; + /* length of signal mask in 4-byte words */ + size = (bytes >= NSIG / 8) ? NSIG / 32 : (bytes + 3) / 4; + + /* check whether 2/3 or more bits are set */ + if (popcount32(mask, size) >= size * 32 * 2 / 3) { + /* show those signals that are NOT in the mask */ + unsigned int j; + for (j = 0; j < size; ++j) + inverted_mask[j] = ~mask[j]; + mask = inverted_mask; *s++ = '~'; + } sep = '['; - for (i = 1; i < maxsigs; i++) { - if (sigismember(mask, i) == show_members) { - *s++ = sep; - if (i < nsignals) { - s = stpcpy(s, signalent[i] + 3); - } + for (i = 0; (i = next_set_bit(mask, i, size * 32)) >= 0; ) { + ++i; + *s++ = sep; + if (i < nsignals) { + s = stpcpy(s, signalent[i] + 3); + } #ifdef SIGRTMIN - else if (i >= __SIGRTMIN && i <= __SIGRTMAX) { - s += sprintf(s, "RT_%u", i - __SIGRTMIN); - } + else if (i >= __SIGRTMIN && i <= __SIGRTMAX) { + s += sprintf(s, "RT_%u", i - __SIGRTMIN); + } #endif - else { - s += sprintf(s, "%u", i); - } - sep = ' '; + else { + s += sprintf(s, "%u", i); } + sep = ' '; } if (sep == '[') *s++ = sep; @@ -277,20 +290,14 @@ sprintsigmask(const char *str, sigset_t *mask) return outstr; } -static const char * -sprintsigmask_long(const char *str, long mask) -{ - sigset_t s; - sigemptyset(&s); - *(long *)&s = mask; - return sprintsigmask(str, &s); -} +#define tprintsigmask_addr(prefix, mask) \ + tprints(sprintsigmask_n((prefix), (mask), sizeof(mask))) -static void -printsigmask(sigset_t *mask) -{ - tprints(sprintsigmask("", mask)); -} +#define sprintsigmask_val(prefix, mask) \ + sprintsigmask_n((prefix), &(mask), sizeof(mask)) + +#define tprintsigmask_val(prefix, mask) \ + tprints(sprintsigmask_n((prefix), &(mask), sizeof(mask))) void printsignal(int nr) @@ -301,7 +308,7 @@ printsignal(int nr) void print_sigset_addr_len(struct tcb *tcp, long addr, long len) { - sigset_t ss; + char mask[NSIG / 8]; if (!addr) { tprints("NULL"); @@ -315,12 +322,14 @@ print_sigset_addr_len(struct tcb *tcp, long addr, long len) tprintf("%#lx", addr); return; } - if (len > NSIG / 8) + if (len >= NSIG / 8) len = NSIG / 8; - sigemptyset(&ss); - if (umoven(tcp, addr, len, (char *)&ss) < 0) + else + len = (len + 3) & ~3; + + if (umoven(tcp, addr, len, mask) < 0) goto bad; - printsigmask(&ss); + tprints(sprintsigmask_n("", mask, len)); } #ifndef ILL_ILLOPC @@ -656,10 +665,10 @@ int sys_sigsetmask(struct tcb *tcp) { if (entering(tcp)) { - tprints(sprintsigmask_long("", tcp->u_arg[0])); + tprintsigmask_val("", tcp->u_arg[0]); } else if (!syserror(tcp)) { - tcp->auxstr = sprintsigmask_long("old mask ", tcp->u_rval); + tcp->auxstr = sprintsigmask_val("old mask ", tcp->u_rval); return RVAL_HEX | RVAL_STR; } return 0; @@ -717,9 +726,9 @@ decode_old_sigaction(struct tcb *tcp, long addr) else tprintf("{%#lx, ", (long) sa.__sa_handler); #ifdef MIPS - tprints(sprintsigmask("", (sigset_t *)sa.sa_mask)); + tprintsigmask_addr("", sa.sa_mask); #else - tprints(sprintsigmask_long("", sa.sa_mask)); + tprintsigmask_val("", sa.sa_mask); #endif tprints(", "); printflags(sigact_flags, sa.sa_flags, "SA_???"); @@ -823,16 +832,13 @@ sys_sigreturn(struct tcb *tcp) /* more fields follow, which we aren't interested in */ }; struct arm_ucontext uc; - sigset_t sigm; if (umove(tcp, arm_regs.ARM_sp, &uc) < 0) return 0; - /* Kernel fills out uc.sc.oldmask too when it sets up signal stack, + /* + * Kernel fills out uc.sc.oldmask too when it sets up signal stack, * but for sigmask restore, sigreturn syscall uses uc.uc_sigmask instead. - * tprints(sprintsigmask_long(") (mask ", uc.sc.oldmask)); */ - sigemptyset(&sigm); - memcpy(&sigm, uc.uc_sigmask, 8); - tprints(sprintsigmask(") (mask ", &sigm)); + tprintsigmask_addr(") (mask ", uc.uc_sigmask); } #elif defined(S390) || defined(S390X) if (entering(tcp)) { @@ -842,7 +848,7 @@ sys_sigreturn(struct tcb *tcp) return 0; if (umove(tcp, usp + __SIGNAL_FRAMESIZE, &sc) < 0) return 0; - tprints(sprintsigmask(") (mask ", (sigset_t *)&sc.oldmask[0])); + tprintsigmask_addr(") (mask ", sc.oldmask); } #elif defined(I386) || defined(X86_64) # if defined(X86_64) @@ -902,31 +908,24 @@ sys_sigreturn(struct tcb *tcp) * and after it an additional u32 extramask[1] which holds * upper half of the mask. */ - union { - sigset_t sig; - uint32_t mask[2]; - } sigmask; + uint32_t sigmask[2]; if (umove(tcp, *i386_esp_ptr, &signal_stack) < 0) return 0; - sigemptyset(&sigmask.sig); - sigmask.mask[0] = signal_stack.sc.oldmask; - sigmask.mask[1] = signal_stack.extramask[0]; - tprints(sprintsigmask(") (mask ", &sigmask.sig)); + sigmask[0] = signal_stack.sc.oldmask; + sigmask[1] = signal_stack.extramask[0]; + tprintsigmask_addr(") (mask ", sigmask); } #elif defined(IA64) if (entering(tcp)) { struct sigcontext sc; long sp; - sigset_t sigm; /* offset of sigcontext in the kernel's sigframe structure: */ # define SIGFRAME_SC_OFFSET 0x90 if (upeek(tcp->pid, PT_R12, &sp) < 0) return 0; if (umove(tcp, sp + 16 + SIGFRAME_SC_OFFSET, &sc) < 0) return 0; - sigemptyset(&sigm); - memcpy(&sigm, &sc.sc_mask, NSIG / 8); - tprints(sprintsigmask(") (mask ", &sigm)); + tprintsigmask_val(") (mask ", sc.sc_mask); } #elif defined(POWERPC) if (entering(tcp)) { @@ -946,7 +945,7 @@ sys_sigreturn(struct tcb *tcp) #endif if (umove(tcp, esp, &sc) < 0) return 0; - tprints(sprintsigmask_long(") (mask ", sc.oldmask)); + tprintsigmask_val(") (mask ", sc.oldmask); } #elif defined(M68K) if (entering(tcp)) { @@ -956,7 +955,7 @@ sys_sigreturn(struct tcb *tcp) return 0; if (umove(tcp, usp, &sc) < 0) return 0; - tprints(sprintsigmask_long(") (mask ", sc.sc_mask)); + tprintsigmask_val(") (mask ", sc.sc_mask); } #elif defined(ALPHA) if (entering(tcp)) { @@ -966,7 +965,7 @@ sys_sigreturn(struct tcb *tcp) return 0; if (umove(tcp, fp, &sc) < 0) return 0; - tprints(sprintsigmask_long(") (mask ", sc.sc_mask)); + tprintsigmask_val(") (mask ", sc.sc_mask); } #elif defined(SPARC) || defined(SPARC64) if (entering(tcp)) { @@ -977,7 +976,7 @@ sys_sigreturn(struct tcb *tcp) perror_msg("sigreturn: umove"); return 0; } - tprints(sprintsigmask_long(") (mask ", si.si_mask)); + tprintsigmask_val(") (mask ", si.si_mask); } #elif defined(LINUX_MIPSN32) || defined(LINUX_MIPSN64) /* This decodes rt_sigreturn. The 64-bit ABIs do not have @@ -985,14 +984,13 @@ sys_sigreturn(struct tcb *tcp) if (entering(tcp)) { long sp; struct ucontext uc; - sigset_t sigm; if (upeek(tcp->pid, REG_SP, &sp) < 0) return 0; /* There are six words followed by a 128-byte siginfo. */ sp = sp + 6 * 4 + 128; if (umove(tcp, sp, &uc) < 0) return 0; - tprints(sprintsigmask_long(") (mask ", *(long *) &uc.uc_sigmask)); + tprintsigmask_val(") (mask ", uc.uc_sigmask); } #elif defined(MIPS) if (entering(tcp)) { @@ -1006,7 +1004,7 @@ sys_sigreturn(struct tcb *tcp) sp = regs.regs[29]; if (umove(tcp, sp, &si) < 0) return 0; - tprints(sprintsigmask_long(") (mask ", si.si_mask)); + tprintsigmask_val(") (mask ", si.si_mask); } #elif defined(CRISV10) || defined(CRISV32) if (entering(tcp)) { @@ -1018,20 +1016,17 @@ sys_sigreturn(struct tcb *tcp) } if (umove(tcp, regs[PT_USP], &sc) < 0) return 0; - tprints(sprintsigmask_long(") (mask ", sc.oldmask)); + tprintsigmask_val(") (mask ", sc.oldmask); } #elif defined(TILE) if (entering(tcp)) { struct ucontext uc; - sigset_t sigm; /* offset of ucontext in the kernel's sigframe structure */ # define SIGFRAME_UC_OFFSET C_ABI_SAVE_AREA_SIZE + sizeof(siginfo_t) if (umove(tcp, tile_regs.sp + SIGFRAME_UC_OFFSET, &uc) < 0) return 0; - sigemptyset(&sigm); - memcpy(&sigm, &uc.uc_sigmask, NSIG / 8); - tprints(sprintsigmask(") (mask ", &sigm)); + tprintsigmask_val(") (mask ", uc.uc_sigmask); } #elif defined(MICROBLAZE) /* TODO: Verify that this is correct... */ @@ -1043,7 +1038,7 @@ sys_sigreturn(struct tcb *tcp) return 0; if (umove(tcp, sp, &sc) < 0) return 0; - tprints(sprintsigmask_long(") (mask ", sc.oldmask)); + tprintsigmask_val(") (mask ", sc.oldmask); } #elif defined(XTENSA) /* Xtensa only has rt_sys_sigreturn */ @@ -1060,7 +1055,7 @@ int sys_siggetmask(struct tcb *tcp) { if (exiting(tcp)) { - tcp->auxstr = sprintsigmask_long("mask ", tcp->u_rval); + tcp->auxstr = sprintsigmask_val("mask ", tcp->u_rval); } return RVAL_HEX | RVAL_STR; } @@ -1069,7 +1064,7 @@ int sys_sigsuspend(struct tcb *tcp) { if (entering(tcp)) { - tprints(sprintsigmask_long("", tcp->u_arg[2])); + tprintsigmask_val("", tcp->u_arg[2]); } return 0; } @@ -1134,10 +1129,10 @@ sys_sigprocmask(struct tcb *tcp) * ret = sigprocmask(how, &new, &old, ...); */ printxval(sigprocmaskcmds, tcp->u_arg[0], "SIG_???"); - tprints(sprintsigmask_long(", ", tcp->u_arg[1])); + tprintsigmask_val(", ", tcp->u_arg[1]); } else if (!syserror(tcp)) { - tcp->auxstr = sprintsigmask_long("old mask ", tcp->u_rval); + tcp->auxstr = sprintsigmask_val("old mask ", tcp->u_rval); return RVAL_HEX | RVAL_STR; } # else /* !ALPHA */ @@ -1244,7 +1239,6 @@ static void decode_new_sigaction(struct tcb *tcp, long addr) { struct new_sigaction sa; - sigset_t sigset; int r; if (!addr) { @@ -1306,9 +1300,7 @@ decode_new_sigaction(struct tcb *tcp, long addr) * with wrong sigset size (just returns EINVAL instead). * We just fetch the right size, which is NSIG / 8. */ - sigemptyset(&sigset); - memcpy(&sigset, &sa.sa_mask, NSIG / 8); - printsigmask(&sigset); + tprintsigmask_val("", sa.sa_mask); tprints(", "); printflags(sigact_flags, sa.sa_flags, "SA_???"); diff --git a/tests/sigaction.awk b/tests/sigaction.awk index 2c4eab6..5bd0568 100644 --- a/tests/sigaction.awk +++ b/tests/sigaction.awk @@ -8,6 +8,11 @@ # the 1st is for any architecture with SA_RESTORER, including SPARC; # the 2nd is for any architecture without SA_RESTORER, including ALPHA. +BEGIN { + lines = 5 + fail = 0 +} + # Test 1. NR == 1 && /^rt_sigaction\(SIGUSR2, {SIG_IGN, \[HUP INT\], SA_RESTORER\|SA_RESTART, 0x[0-9a-f]+}, {SIG_DFL, \[\], 0}, (0x[0-9a-f]+, )?(4|8|16)\) = 0$/ {next} NR == 1 && /^rt_sigaction\(SIGUSR2, {SIG_IGN, \[HUP INT\], SA_RESTART}, {SIG_DFL, \[\], 0}, (4|8|16)(, 0x[0-9a-f]+)?\) = 0$/ {next} @@ -20,17 +25,26 @@ NR == 2 && /^rt_sigaction\(SIGUSR2, {0x[0-9a-f]+, \[QUIT TERM\], SA_SIGINFO}, {S NR == 3 && /^rt_sigaction\(SIGUSR2, {SIG_DFL, \[\], SA_RESTORER, 0x[0-9a-f]+}, {0x[0-9a-f]+, \[QUIT TERM\], SA_RESTORER\|SA_SIGINFO, 0x[0-9a-f]+}, (0x[0-9a-f]+, )?(4|8|16)\) = 0$/ {next} NR == 3 && /^rt_sigaction\(SIGUSR2, {SIG_DFL, \[\], 0}, {0x[0-9a-f]+, \[QUIT TERM\], SA_SIGINFO}, (4|8|16)(, 0x[0-9a-f]+)?\) = 0$/ {next} +# Test 4. +NR == 4 && /^rt_sigaction\(SIGUSR2, {SIG_DFL, ~\[HUP( ((RT|SIGRT)[^] ]+|[3-9][0-9]|1[0-9][0-9]))*\], SA_RESTORER, 0x[0-9a-f]+}, {SIG_DFL, \[\], SA_RESTORER, 0x[0-9a-f]+}, (0x[0-9a-f]+, )?(4|8|16)\) = 0$/ {next} +NR == 4 && /^rt_sigaction\(SIGUSR2, {SIG_DFL, ~\[HUP( ((RT|SIGRT)[^] ]+|[3-9][0-9]|1[0-9][0-9]))*\], 0}, {SIG_DFL, \[\], 0}, (4|8|16)(, 0x[0-9a-f]+)?\) = 0$/ {next} + # The last line. -NR == 4 && /^\+\+\+ exited with 0 \+\+\+$/ {next} +NR == lines && /^\+\+\+ exited with 0 \+\+\+$/ {next} { print "Line " NR " does not match: " $0 - exit 1 + fail=1 } END { - if (NR != 4) { - print "Expected 4 lines, found " NR " line(s)." + if (NR != lines) { + print "Expected " lines " lines, found " NR " line(s)." + print "" + exit 1 + } + if (fail) { + print "" exit 1 } } diff --git a/tests/sigaction.c b/tests/sigaction.c index 82666f9..b5f19b5 100644 --- a/tests/sigaction.c +++ b/tests/sigaction.c @@ -11,26 +11,30 @@ static void handle_signal(int no) int main(void) { - struct sigaction sa, sa1, sa2, sa3; + struct sigaction sa, sa0; sa.sa_handler = SIG_IGN; sigemptyset(&sa.sa_mask); sigaddset(&sa.sa_mask, SIGHUP); sigaddset(&sa.sa_mask, SIGINT); sa.sa_flags = SA_RESTART; - assert(!sigaction(SIGUSR2, &sa, &sa1)); + assert(!sigaction(SIGUSR2, &sa, &sa0)); sa.sa_handler = handle_signal; sigemptyset(&sa.sa_mask); sigaddset(&sa.sa_mask, SIGQUIT); sigaddset(&sa.sa_mask, SIGTERM); sa.sa_flags = SA_SIGINFO; - assert(!sigaction(SIGUSR2, &sa, &sa2)); + assert(!sigaction(SIGUSR2, &sa, &sa0)); sa.sa_handler = SIG_DFL; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; - assert(!sigaction(SIGUSR2, &sa, &sa3)); + assert(!sigaction(SIGUSR2, &sa, &sa0)); + + sigfillset(&sa.sa_mask); + sigdelset(&sa.sa_mask, SIGHUP); + assert(!sigaction(SIGUSR2, &sa, &sa0)); return 0; } -- cgit v1.2.3