about summary refs log tree commit diff
path: root/gmon
diff options
context:
space:
mode:
Diffstat (limited to 'gmon')
-rw-r--r--gmon/Makefile22
-rw-r--r--gmon/gmon.c29
-rw-r--r--gmon/mcount.c5
-rw-r--r--gmon/sys/gmon.h6
-rw-r--r--gmon/tst-mcount-overflow-check.sh45
-rw-r--r--gmon/tst-mcount-overflow.c72
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;
+}