[PATCH] Rewrite signal mask decoding without sigset_t
Dmitry V. Levin
ldv at altlinux.org
Wed Feb 26 23:36:23 UTC 2014
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.
---
Tested on x86, x86-64 and arm. Please test on other architectures,
especially on big-endian, I'm not sure I haven't broken some of them.
At least sigaction.test should pass everywhere.
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 <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])
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..894ee9a 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)[^] ]+)*\], 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)[^] ]+)*\], 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;
}
--
ldv
More information about the Strace-devel
mailing list