]> granicus.if.org Git - strace/commitdiff
Rewrite signal mask decoding without sigset_t
authorDmitry V. Levin <ldv@altlinux.org>
Wed, 26 Feb 2014 16:51:28 +0000 (16:51 +0000)
committerDmitry V. Levin <ldv@altlinux.org>
Thu, 27 Feb 2014 00:28:39 +0000 (00:28 +0000)
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
signal.c
tests/sigaction.awk
tests/sigaction.c

index 75eafc8ad78cbc0de23e2203c38ca3f45d5d51d9..caa5aeddfb61cff2d02368a2d0e12862d483bf13 100644 (file)
@@ -309,6 +309,15 @@ if test "x$st_cv_sa_restorer" != xno; then
                           [SA_RESTORER defined in <asm/signal.h>])
 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])
index e2c929f7114aed4dcebceaf4da8a4cb61880f4ab..a13f7ace33ed4187a7c8ee9baf0e5a73980e7f3a 100644 (file)
--- 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_???");
index 2c4eab6877a21deaea1b0ffed50d6e333fadbe8c..5bd0568650d40b3605a9aeaa66463e85054001bf 100644 (file)
@@ -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
   }
 }
index 82666f9e53bcab52489ba6f64fcdf793e01e7ef9..b5f19b561e8663b06e6cb7be9d9e20f61a9d7ff8 100644 (file)
@@ -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;
 }