diff options
Diffstat (limited to 'gmon')
-rw-r--r-- | gmon/Makefile | 22 | ||||
-rw-r--r-- | gmon/gmon.c | 29 | ||||
-rw-r--r-- | gmon/mcount.c | 5 | ||||
-rw-r--r-- | gmon/sys/gmon.h | 6 | ||||
-rw-r--r-- | gmon/tst-mcount-overflow-check.sh | 45 | ||||
-rw-r--r-- | gmon/tst-mcount-overflow.c | 72 |
6 files changed, 172 insertions, 7 deletions
diff --git a/gmon/Makefile b/gmon/Makefile index 4dd5adb80b..83837dd689 100644 --- a/gmon/Makefile +++ b/gmon/Makefile @@ -25,7 +25,7 @@ include ../Makeconfig headers := sys/gmon.h sys/gmon_out.h sys/profil.h routines := gmon mcount profil sprofil prof-freq -tests = tst-sprofil tst-gmon +tests = tst-sprofil tst-gmon tst-mcount-overflow ifeq ($(build-profile),yes) tests += tst-profile-static tests-static += tst-profile-static @@ -56,6 +56,18 @@ ifeq ($(run-built-tests),yes) tests-special += $(objpfx)tst-gmon-gprof.out endif +CFLAGS-tst-mcount-overflow.c := -fno-omit-frame-pointer -pg +tst-mcount-overflow-no-pie = yes +CRT-tst-mcount-overflow := $(csu-objpfx)g$(start-installed-name) +# Intentionally use invalid config where maxarcs<minarcs to check warning is printed +tst-mcount-overflow-ENV := GMON_OUT_PREFIX=$(objpfx)tst-mcount-overflow.data \ + GLIBC_TUNABLES=glibc.gmon.minarcs=51:glibc.gmon.maxarcs=50 +# Send stderr into output file because we make sure expected messages are printed +tst-mcount-overflow-ARGS := 2>&1 1>/dev/null | cat +ifeq ($(run-built-tests),yes) +tests-special += $(objpfx)tst-mcount-overflow-check.out +endif + CFLAGS-tst-gmon-static.c := $(PIE-ccflag) -fno-omit-frame-pointer -pg CRT-tst-gmon-static := $(csu-objpfx)g$(static-start-installed-name) tst-gmon-static-no-pie = yes @@ -103,6 +115,14 @@ $(objpfx)tst-gmon.out: clean-tst-gmon-data clean-tst-gmon-data: rm -f $(objpfx)tst-gmon.data.* +$(objpfx)tst-mcount-overflow.o: clean-tst-mcount-overflow-data +clean-tst-mcount-overflow-data: + rm -f $(objpfx)tst-mcount-overflow.data.* + +$(objpfx)tst-mcount-overflow-check.out: tst-mcount-overflow-check.sh $(objpfx)tst-mcount-overflow.out + $(SHELL) $< $(objpfx)tst-mcount-overflow > $@; \ + $(evaluate-test) + $(objpfx)tst-gmon-gprof.out: tst-gmon-gprof.sh $(objpfx)tst-gmon.out $(SHELL) $< $(GPROF) $(objpfx)tst-gmon $(objpfx)tst-gmon.data.* > $@; \ $(evaluate-test) diff --git a/gmon/gmon.c b/gmon/gmon.c index bf76358d5b..689bf80141 100644 --- a/gmon/gmon.c +++ b/gmon/gmon.c @@ -46,6 +46,11 @@ #include <libc-internal.h> #include <not-cancel.h> +#if HAVE_TUNABLES +# define TUNABLE_NAMESPACE gmon +# include <elf/dl-tunables.h> +#endif + #ifdef PIC # include <link.h> @@ -124,6 +129,22 @@ __monstartup (u_long lowpc, u_long highpc) int o; char *cp; struct gmonparam *p = &_gmonparam; + long int minarcs, maxarcs; + +#if HAVE_TUNABLES + /* Read minarcs/maxarcs tunables. */ + minarcs = TUNABLE_GET (minarcs, int32_t, NULL); + maxarcs = TUNABLE_GET (maxarcs, int32_t, NULL); + if (maxarcs < minarcs) + { + ERR("monstartup: maxarcs < minarcs, setting maxarcs = minarcs\n"); + maxarcs = minarcs; + } +#else + /* No tunables, we use hardcoded defaults */ + minarcs = MINARCS; + maxarcs = MAXARCS; +#endif /* * round lowpc and highpc to multiples of the density we're using @@ -146,10 +167,10 @@ __monstartup (u_long lowpc, u_long highpc) } p->fromssize = ROUNDUP(p->textsize / HASHFRACTION, sizeof(*p->froms)); p->tolimit = p->textsize * ARCDENSITY / 100; - if (p->tolimit < MINARCS) - p->tolimit = MINARCS; - else if (p->tolimit > MAXARCS) - p->tolimit = MAXARCS; + if (p->tolimit < minarcs) + p->tolimit = minarcs; + else if (p->tolimit > maxarcs) + p->tolimit = maxarcs; p->tossize = p->tolimit * sizeof(struct tostruct); cp = calloc (p->kcountsize + p->fromssize + p->tossize, 1); diff --git a/gmon/mcount.c b/gmon/mcount.c index 9d4a1a50fa..f7180fdb83 100644 --- a/gmon/mcount.c +++ b/gmon/mcount.c @@ -41,6 +41,10 @@ static char sccsid[] = "@(#)mcount.c 8.1 (Berkeley) 6/4/93"; #include <atomic.h> +#include <not-cancel.h> +#include <unistd.h> +#define ERR(s) __write_nocancel (STDERR_FILENO, s, sizeof (s) - 1) + /* * mcount is called on entry to each function compiled with the profiling * switch set. _mcount(), which is declared in a machine-dependent way @@ -170,6 +174,7 @@ done: return; overflow: p->state = GMON_PROF_ERROR; + ERR("mcount: call graph buffer size limit exceeded, gmon.out will not be generated\n"); return; } diff --git a/gmon/sys/gmon.h b/gmon/sys/gmon.h index b4cc3b043a..af0582a371 100644 --- a/gmon/sys/gmon.h +++ b/gmon/sys/gmon.h @@ -111,6 +111,8 @@ extern struct __bb *__bb_head; * Always allocate at least this many tostructs. This * hides the inadequacy of the ARCDENSITY heuristic, at least * for small programs. + * + * Value can be overridden at runtime by glibc.gmon.minarcs tunable. */ #define MINARCS 50 @@ -124,8 +126,8 @@ extern struct __bb *__bb_head; * Used to be max representable value of ARCINDEX minus 2, but now * that ARCINDEX is a long, that's too large; we don't really want * to allow a 48 gigabyte table. - * The old value of 1<<16 wasn't high enough in practice for large C++ - * programs; will 1<<20 be adequate for long? FIXME + * + * Value can be overridden at runtime by glibc.gmon.maxarcs tunable. */ #define MAXARCS (1 << 20) diff --git a/gmon/tst-mcount-overflow-check.sh b/gmon/tst-mcount-overflow-check.sh new file mode 100644 index 0000000000..27eb5538fd --- /dev/null +++ b/gmon/tst-mcount-overflow-check.sh @@ -0,0 +1,45 @@ +#!/bin/sh +# Test expected messages generated when mcount overflows +# Copyright (C) 2017-2023 Free Software Foundation, Inc. +# Copyright The GNU Toolchain Authors. +# 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 +# <https://www.gnu.org/licenses/>. + +LC_ALL=C +export LC_ALL +set -e +exec 2>&1 + +program="$1" + +check_msg() { + if ! grep -q "$1" "$program.out"; then + echo "FAIL: expected message not in output: $1" + exit 1 + fi +} + +check_msg 'monstartup: maxarcs < minarcs, setting maxarcs = minarcs' +check_msg 'mcount: call graph buffer size limit exceeded, gmon.out will not be generated' + +for data_file in $1.data.*; do + if [ -f "$data_file" ]; then + echo "FAIL: expected no data files, but found $data_file" + exit 1 + fi +done + +echo PASS diff --git a/gmon/tst-mcount-overflow.c b/gmon/tst-mcount-overflow.c new file mode 100644 index 0000000000..06cc93ef87 --- /dev/null +++ b/gmon/tst-mcount-overflow.c @@ -0,0 +1,72 @@ +/* Test program to trigger mcount overflow in profiling collection. + Copyright (C) 2017-2023 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 + <https://www.gnu.org/licenses/>. */ + +/* Program with sufficiently complex, yet pointless, call graph + that it will trigger an mcount overflow, when you set the + minarcs/maxarcs tunables to very low values. */ + +#define PREVENT_TAIL_CALL asm volatile ("") + +/* Calls REP(n) macro 16 times, for n=0..15. + * You need to define REP(n) before using this. + */ +#define REPS \ + REP(0) REP(1) REP(2) REP(3) REP(4) REP(5) REP(6) REP(7) \ + REP(8) REP(9) REP(10) REP(11) REP(12) REP(13) REP(14) REP(15) + +/* Defines 16 leaf functions named f1_0 to f1_15 */ +#define REP(n) \ + __attribute__ ((noinline, noclone, weak)) void f1_##n (void) {}; +REPS +#undef REP + +/* Calls all 16 leaf functions f1_* in succession */ +__attribute__ ((noinline, noclone, weak)) void +f2 (void) +{ +# define REP(n) f1_##n(); + REPS +# undef REP + PREVENT_TAIL_CALL; +} + +/* Defines 16 functions named f2_0 to f2_15, which all just call f2 */ +#define REP(n) \ + __attribute__ ((noinline, noclone, weak)) void \ + f2_##n (void) { f2(); PREVENT_TAIL_CALL; }; +REPS +#undef REP + +__attribute__ ((noinline, noclone, weak)) void +f3 (int count) +{ + for (int i = 0; i < count; ++i) + { + /* Calls f1_0(), f2_0(), f1_1(), f2_1(), f3_0(), etc */ +# define REP(n) f1_##n(); f2_##n(); + REPS +# undef REP + } +} + +int +main (void) +{ + f3 (1000); + return 0; +} |