[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