From patchwork Sat Dec 8 16:14:02 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Florian Weimer X-Patchwork-Id: 30589 Received: (qmail 122524 invoked by alias); 8 Dec 2018 16:14:13 -0000 Mailing-List: contact libc-alpha-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-alpha-owner@sourceware.org Delivered-To: mailing list libc-alpha@sourceware.org Received: (qmail 122493 invoked by uid 89); 8 Dec 2018 16:14:12 -0000 Authentication-Results: sourceware.org; auth=none X-Spam-SWARE-Status: No, score=-26.9 required=5.0 tests=BAYES_00, GIT_PATCH_0, GIT_PATCH_1, GIT_PATCH_2, GIT_PATCH_3, KAM_SHORT, SPF_HELO_PASS autolearn=ham version=3.3.2 spammy=measure, fweimer@redhat.com, sk:fweimer, weimer X-HELO: mx1.redhat.com From: Florian Weimer To: libc-alpha@sourceware.org Subject: [PATCH] nptl: New test nptl/tst-stack-usage Date: Sat, 08 Dec 2018 17:14:02 +0100 Message-ID: <87mupg9et1.fsf@oldenburg2.str.redhat.com> User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/26.1 (gnu/linux) MIME-Version: 1.0 Results for x86-64 with AVX2 (kernel-4.19.5-300.fc29): info: stack size: 524288 info: PTHREAD_STACK_MIN: 16384 info: MINSIGSTKSZ: 2048 info: SIGSTKSZ: 8192 info: measuring stack usage, no signal delivery lowest modification offset ranges from 519904 to 519920 highest modification offset ranges from 523675 to 524288 minimum modified range: 3755 maximum modified range: 4384 info: measuring stack usage, with signal delivery lowest modification offset ranges from 517944 to 517945 highest modification offset ranges from 523675 to 524288 minimum modified range: 5731 maximum modified range: 6344 info: estimated signal delivery stack overhead: 1960 bytes info: measuring stack usage, no-cancel test lowest modification offset ranges from 519720 to 519736 highest modification offset ranges from 523675 to 524288 minimum modified range: 3939 maximum modified range: 4568 info: measuring stack usage, cancellation test lowest modification offset ranges from 516048 to 516049 highest modification offset ranges from 523675 to 524288 minimum modified range: 7627 maximum modified range: 8240 info: estimated cancellation stack overhead: 3672 bytes Results for x86-64 with AVX-512F (kernel-4.18.0-48.el8): info: stack size: 524288 info: PTHREAD_STACK_MIN: 16384 info: MINSIGSTKSZ: 2048 info: SIGSTKSZ: 8192 info: measuring stack usage, no signal delivery lowest modification offset ranges from 519920 to 519936 highest modification offset ranges from 523675 to 524288 minimum modified range: 3739 maximum modified range: 4368 info: measuring stack usage, with signal delivery lowest modification offset ranges from 516472 to 516473 highest modification offset ranges from 523675 to 524288 minimum modified range: 7202 maximum modified range: 7816 info: estimated signal delivery stack overhead: 3448 bytes info: measuring stack usage, no-cancel test lowest modification offset ranges from 519736 to 519752 highest modification offset ranges from 523675 to 524288 minimum modified range: 3923 maximum modified range: 4552 info: measuring stack usage, cancellation test lowest modification offset ranges from 514448 to 518120 highest modification offset ranges from 523675 to 524288 minimum modified range: 6168 maximum modified range: 9840 info: estimated cancellation stack overhead: 5288 bytes Results for POWER8 BE (kernel-3.10.0-957.1.3.el7): info: stack size: 524288 info: PTHREAD_STACK_MIN: 131072 info: MINSIGSTKSZ: 4096 info: SIGSTKSZ: 16384 info: measuring stack usage, no signal delivery lowest modification offset ranges from 517824 to 517826 highest modification offset ranges from 522528 to 522624 minimum modified range: 4702 maximum modified range: 4800 info: measuring stack usage, with signal delivery lowest modification offset ranges from 513232 to 513234 highest modification offset ranges from 522528 to 522624 minimum modified range: 9294 maximum modified range: 9392 info: estimated signal delivery stack overhead: 4592 bytes info: measuring stack usage, no-cancel test lowest modification offset ranges from 516976 to 516978 highest modification offset ranges from 522528 to 522624 minimum modified range: 5550 maximum modified range: 5648 info: measuring stack usage, cancellation test lowest modification offset ranges from 503600 to 503602 highest modification offset ranges from 522528 to 522624 minimum modified range: 18926 maximum modified range: 19024 info: estimated cancellation stack overhead: 13376 bytes Results for POWER9 LE (kernel-4.18.0-48.el8): info: stack size: 524288 info: PTHREAD_STACK_MIN: 131072 info: MINSIGSTKSZ: 4096 info: SIGSTKSZ: 16384 info: measuring stack usage, no signal delivery lowest modification offset ranges from 517984 to 517985 highest modification offset ranges from 522526 to 522624 minimum modified range: 4542 maximum modified range: 4640 info: measuring stack usage, with signal delivery lowest modification offset ranges from 513472 to 513473 highest modification offset ranges from 522526 to 522624 minimum modified range: 9054 maximum modified range: 9152 info: estimated signal delivery stack overhead: 4512 bytes info: measuring stack usage, no-cancel test lowest modification offset ranges from 517296 to 517297 highest modification offset ranges from 522526 to 522624 minimum modified range: 5230 maximum modified range: 5328 info: measuring stack usage, cancellation test lowest modification offset ranges from 506144 to 506145 highest modification offset ranges from 522526 to 522624 minimum modified range: 16381 maximum modified range: 16480 info: estimated cancellation stack overhead: 11152 bytes 2018-12-08 Florian Weimer * nptl/tst-stack-usage.c: New file. * nptl/Makefile (tests): Add tst-stack-usage. (tst-stack-usage): Link with -z now. * support/Makefile (libsupport-routines): Add xpthread_attr_setstack. * support/xthread.h (xpthread_attr_setstack): Declare. * support/xpthread_attr_setstack.c: New file. diff --git a/nptl/Makefile b/nptl/Makefile index 34ae830276..f994db59cc 100644 --- a/nptl/Makefile +++ b/nptl/Makefile @@ -318,7 +318,8 @@ tests = tst-attr1 tst-attr2 tst-attr3 tst-default-attr \ tst-minstack-throw \ tst-cnd-basic tst-mtx-trylock tst-cnd-broadcast \ tst-cnd-timedwait tst-thrd-detach tst-mtx-basic tst-thrd-sleep \ - tst-mtx-recursive tst-tss-basic tst-call-once tst-mtx-timedlock + tst-mtx-recursive tst-tss-basic tst-call-once tst-mtx-timedlock \ + tst-stack-usage tests-internal := tst-rwlock19 tst-rwlock20 \ tst-sem11 tst-sem12 tst-sem13 \ @@ -722,6 +723,9 @@ $(objpfx)tst-audit-threads: $(objpfx)tst-audit-threads-mod2.so $(objpfx)tst-audit-threads.out: $(objpfx)tst-audit-threads-mod1.so tst-audit-threads-ENV = LD_AUDIT=$(objpfx)tst-audit-threads-mod1.so +# Disable lazy binding to avoid measuring ld.so stack overhead. +LDFLAGS-tst-stack-usage = -Wl,-z,now + # The tests here better do not run in parallel ifneq ($(filter %tests,$(MAKECMDGOALS)),) .NOTPARALLEL: diff --git a/nptl/tst-stack-usage.c b/nptl/tst-stack-usage.c new file mode 100644 index 0000000000..80f853f595 --- /dev/null +++ b/nptl/tst-stack-usage.c @@ -0,0 +1,238 @@ +/* Measure the stack size used by signal delivery and thread cancellation. + Copyright (C) 2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Initialized by do_test below. */ +static unsigned char *stack_base; +static size_t stack_size; +static pthread_attr_t stack_attr; +static sigset_t sigusr1_set; + +/* Capture the range of a variable. */ +struct range_statistics +{ + size_t min; + size_t max; +}; + +/* Initialize a struct range_statistics object. */ +static inline struct range_statistics +range_statistics_create (void) +{ + return (struct range_statistics) { .min = -1, .max = 0 }; +} + +/* Update the statistics object with the value. */ +static inline void +range_statistics_update (struct range_statistics *stats, size_t value) +{ + if (value < stats->min) + stats->min = value; + if (value > stats->max) + stats->max = value; +} + +/* Used to capture stack usage information. */ +static int canary_value; +struct range_statistics untouched_low_stats; +struct range_statistics untouched_high_stats; +struct range_statistics touched_range_stats; + + +/* Examine the stack at stack_base for traces of usage. This must be + called from the thread start routine because during thread + destruction, madvise is called on the user-supplied stack with + MADV_DONTNEED. */ +static void * +capture_stack_usage (void) +{ + /* Computed the untouched poritions of the stack. */ + size_t untouched_low = 0; + while (untouched_low < stack_size + && stack_base[untouched_low] == canary_value) + ++untouched_low; + size_t untouched_high = stack_size; + while (untouched_high > 0 + && stack_base[untouched_high - 1] == canary_value) + --untouched_high; + TEST_VERIFY (untouched_high > untouched_low); + + range_statistics_update (&untouched_low_stats, untouched_low); + range_statistics_update (&untouched_high_stats, untouched_high); + size_t used = untouched_high - untouched_low; + range_statistics_update (&touched_range_stats, used); + return NULL; +} + +/* Wrapper for capture_stack_usage for use from a cancellation + handler. */ +static void +capture_on_cancel (void *closure) +{ + capture_stack_usage (); +} + +/* Attempt to determine stack usage by running THREADFUNC. Print + statistics and return the best estimate for the stack usage. If + OPERATE is not NULL, call it on the thread in question before + joining it. */ +static size_t +measure_stack (const char *what, void *(*threadfunc) (void *), + void (*operate) (pthread_t)) +{ + untouched_low_stats = range_statistics_create (); + untouched_high_stats = range_statistics_create (); + touched_range_stats = range_statistics_create (); + + /* Use different canary values to cover usage scenarios which write + the same value as the canary. */ + for (canary_value = 0; canary_value <= 255; ++canary_value) + { + memset (stack_base, canary_value, stack_size); + pthread_t thr = xpthread_create (&stack_attr, threadfunc, operate); + if (operate != NULL) + operate (thr); + xpthread_join (thr); + } + + printf ("info: measuring stack usage, %s\n", what); + printf (" lowest modification offset ranges from %zu to %zu\n", + untouched_low_stats.min, untouched_low_stats.max); + printf (" highest modification offset ranges from %zu to %zu\n", + untouched_high_stats.min, untouched_high_stats.max); + printf (" minimum modified range: %zu\n", touched_range_stats.min); + printf (" maximum modified range: %zu\n", touched_range_stats.max); + + return touched_range_stats.max; +} + +static void +noop_signal_handler (int signo) +{ +} + +/* Used as the SIGUSR1 signal handler. */ +static void * +noop_threadfunc (void *closure) +{ + return capture_stack_usage (); +} + +/* Thread start routine for signal handler test. */ +static void * +signal_threadfunc (void *closure) +{ + xpthread_sigmask (SIG_UNBLOCK, &sigusr1_set, NULL); + /* Delivery is synchronous, to this thread, because the signal is + blocked on all other threads. */ + raise (SIGUSR1); + return capture_stack_usage (); +} + +/* Used for synchronization in the cancellation thread. */ +static pthread_barrier_t barrier; + +/* Cause cancel_threadfunc to return normally. */ +static void +operate_no_cancel (pthread_t thr) +{ + xpthread_barrier_wait (&barrier); +} + +/* Cancel the cancel_threadfunc thread. */ +static void +operate_cancel (pthread_t thr) +{ + xpthread_barrier_wait (&barrier); + xpthread_cancel (thr); +} + +/* Thread start routine for cancellation tests. */ +static void * +cancel_threadfunc (void *closure) +{ + /* Synchronization here prevents cancellation in the thread + initialization. */ + xpthread_barrier_wait (&barrier); + pthread_cleanup_push (capture_on_cancel, NULL); + if (closure == operate_cancel) + /* Wait for cancellation. */ + pause (); + pthread_cleanup_pop (1); + return NULL; +} + +static int +do_test (void) +{ + /* This should be large enough for all architectures. */ + stack_size = 512 * 1024; + printf ("info: stack size: %zu\n", stack_size); + printf ("info: PTHREAD_STACK_MIN: %zu\n", (size_t) PTHREAD_STACK_MIN); + printf ("info: MINSIGSTKSZ: %zu\n", (size_t) MINSIGSTKSZ); + printf ("info: SIGSTKSZ: %zu\n", (size_t) SIGSTKSZ); + TEST_VERIFY (stack_size >= PTHREAD_STACK_MIN); + + stack_base = xmmap (NULL, stack_size, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1); + xpthread_attr_init (&stack_attr); + xpthread_attr_setstack (&stack_attr, stack_base, stack_size); + + /* Block the signal on all threads, so that we can force delivery on + a specific thread. */ + sigemptyset (&sigusr1_set); + sigaddset (&sigusr1_set, SIGUSR1); + xpthread_sigmask (SIG_BLOCK, &sigusr1_set, NULL); + xsignal (SIGUSR1, noop_signal_handler); + + size_t noop = measure_stack ("no signal delivery", noop_threadfunc, NULL); + TEST_VERIFY (noop <= PTHREAD_STACK_MIN); + size_t with_signal + = measure_stack ("with signal delivery", signal_threadfunc, NULL); + TEST_VERIFY (with_signal <= PTHREAD_STACK_MIN); + TEST_VERIFY_EXIT (with_signal >= noop); + printf ("info: estimated signal delivery stack overhead: %zu bytes\n", + with_signal - noop); + + xpthread_barrier_init (&barrier, NULL, 2); + noop = measure_stack ("no-cancel test", cancel_threadfunc, + operate_no_cancel); + TEST_VERIFY (noop <= PTHREAD_STACK_MIN); + size_t with_cancel = measure_stack ("cancellation test", cancel_threadfunc, + operate_cancel); + TEST_VERIFY (with_cancel <= PTHREAD_STACK_MIN); + xpthread_barrier_destroy (&barrier); + TEST_VERIFY_EXIT (with_cancel >= noop); + printf ("info: estimated cancellation stack overhead: %zu bytes\n", + with_cancel - noop); + + xpthread_attr_destroy (&stack_attr); + xmunmap (stack_base, stack_size); + + return 0; +} + +#include diff --git a/support/Makefile b/support/Makefile index 93a5143016..c322c5efac 100644 --- a/support/Makefile +++ b/support/Makefile @@ -103,6 +103,7 @@ libsupport-routines = \ xpthread_attr_init \ xpthread_attr_setdetachstate \ xpthread_attr_setguardsize \ + xpthread_attr_setstack \ xpthread_attr_setstacksize \ xpthread_barrier_destroy \ xpthread_barrier_init \ diff --git a/support/xpthread_attr_setstack.c b/support/xpthread_attr_setstack.c new file mode 100644 index 0000000000..8b7f6fe5f2 --- /dev/null +++ b/support/xpthread_attr_setstack.c @@ -0,0 +1,26 @@ +/* pthread_attr_setstack with error checking. + Copyright (C) 2017-2018 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#include + +void +xpthread_attr_setstack (pthread_attr_t *attr, void *addr, size_t size) +{ + xpthread_check_return ("pthread_attr_setstack", + pthread_attr_setstack (attr, addr, size)); +} diff --git a/support/xthread.h b/support/xthread.h index 623f5ad0ac..209b41c2be 100644 --- a/support/xthread.h +++ b/support/xthread.h @@ -70,6 +70,7 @@ void xpthread_attr_setdetachstate (pthread_attr_t *attr, int detachstate); void xpthread_attr_setstacksize (pthread_attr_t *attr, size_t stacksize); +void xpthread_attr_setstack (pthread_attr_t *attr, void *, size_t); void xpthread_attr_setguardsize (pthread_attr_t *attr, size_t guardsize);