This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[tree-ssa libmudflap] new features
- From: "Frank Ch. Eigler" <fche at redhat dot com>
- To: gcc-patches at gcc dot gnu dot org
- Date: Fri, 28 Mar 2003 17:33:08 -0500
- Subject: [tree-ssa libmudflap] new features
Hi -
The attached patch adds some new features to libmudflap.
It tweaks an internal adaptive algorithm to better suit
an application's run-time access patterns. There's also
an option to intercept SIGUSR1 and print a runtime status
report while an instrumented program is running. There
are some minor tweaks too.
PS. It looks like there will be a talk on mudflap at the
GCC Summit in Ottawa. If anyone is interested in reviewing
drafts of a paper I'm writing about it, let me know.
- FChE
+2003-03-28 Frank Ch. Eigler <fche at redhat dot com>
+
+ * configure.in: Check for target gettimeofday, signal, some headers.
+ * mf-impl.h (__mf_opts): Add new "sigusr1_report" field. Comment
+ out inop multi_threaded field.
+ * mf-runtime.c (options): Handle new "-sigusr1-report" option.
+ (__mf_set_options): Correct handling of "-help".
+ (__mf_sigusr1_respond): New function to manage SIGUSR1 response.
+ (__mf_check, __mf_register, __mf_unregister): Call it.
+ (__mf_insert_new_object, __mf_unregister): Respect HAVE_GETTIMEOFDAY.
+ (__mf_report_leaks): Make callable
+ (__mf_tree_analyze): Traverse in-order. Accumulate address bit
+ distribution statistics.
+ (__mf_adapt_cache): Rewrite shift guessing logic based on address
+ bit distributions.
+ * config.h.in, configure: Regenerated.
+ * testsuite/libmudflap.c/fail27-frag.c: New test.
+ * testsuite/libmudflap.c/pass36-frag.c: New test.
Index: configure.in
===================================================================
RCS file: /cvs/gcc/gcc/libmudflap/Attic/configure.in,v
retrieving revision 1.1.2.11
diff -u -r1.1.2.11 configure.in
--- configure.in 11 Mar 2003 22:48:29 -0000 1.1.2.11
+++ configure.in 28 Mar 2003 22:26:37 -0000
@@ -25,11 +25,10 @@
AC_SUBST(enable_shared)
AC_SUBST(enable_static)
-dnl Should check for this header for the host/target, not the build platform
AC_CHECK_HEADER(stdint.h, [MF_HAVE_STDINT_H=1], [MF_HAVE_STDINT_H=0])
AC_SUBST(MF_HAVE_STDINT_H)
-AC_CHECK_HEADERS(stdint.h execinfo.h)
-AC_CHECK_FUNCS(backtrace backtrace_symbols)
+AC_CHECK_HEADERS(stdint.h execinfo.h signal.h)
+AC_CHECK_FUNCS(backtrace backtrace_symbols gettimeofday signal)
if ${CONFIG_SHELL-/bin/sh} ./libtool --tag CC --features |
grep "enable shared" > /dev/null; then
Index: mf-impl.h
===================================================================
RCS file: /cvs/gcc/gcc/libmudflap/Attic/mf-impl.h,v
retrieving revision 1.1.2.14
diff -u -r1.1.2.14 mf-impl.h
--- mf-impl.h 10 Mar 2003 22:29:58 -0000 1.1.2.14
+++ mf-impl.h 28 Mar 2003 22:26:37 -0000
@@ -84,6 +84,9 @@
/* Collect and emit statistics. */
unsigned collect_stats;
+ /* Set up a SIGUSR1 -> __mf_report handler. */
+ unsigned sigusr1_report;
+
/* Execute internal checking code. */
unsigned internal_checking;
@@ -108,8 +111,8 @@
/* Emit internal tracing message. */
unsigned verbose_trace;
- /* Support multiple threads. */
- unsigned multi_threaded;
+ /* Support multiple threads. XXX: not yet implemented. */
+ /* unsigned multi_threaded; */
/* Wipe stack/heap objects upon unwind. */
unsigned wipe_stack;
Index: mf-runtime.c
===================================================================
RCS file: /cvs/gcc/gcc/libmudflap/Attic/mf-runtime.c,v
retrieving revision 1.1.2.28
diff -u -r1.1.2.28 mf-runtime.c
--- mf-runtime.c 10 Mar 2003 22:29:58 -0000 1.1.2.28
+++ mf-runtime.c 28 Mar 2003 22:26:38 -0000
@@ -16,6 +16,9 @@
#ifdef HAVE_EXECINFO_H
#include <execinfo.h>
#endif
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif
#include <assert.h>
#include <string.h>
@@ -108,7 +111,7 @@
__mf_opts.abbreviate = 1;
__mf_opts.check_initialization = 1;
__mf_opts.verbose_violations = 1;
- __mf_opts.multi_threaded = 0;
+ /* __mf_opts.multi_threaded = 0; */
__mf_opts.free_queue_length = 4;
__mf_opts.persistent_count = 100;
__mf_opts.crumple_zone = 32;
@@ -169,6 +172,11 @@
{"collect-stats",
"collect statistics on mudflap's operation",
set_option, 1, &__mf_opts.collect_stats},
+#if HAVE_SIGNAL
+ {"sigusr1-report",
+ "print report upon SIGUSR1",
+ set_option, 1, &__mf_opts.sigusr1_report},
+#endif
{"internal-checking",
"perform more expensive internal checking",
set_option, 1, &__mf_opts.internal_checking},
@@ -188,9 +196,11 @@
"abbreviate repetitive listings",
set_option, 1, &__mf_opts.abbreviate},
/* XXX: this should be sensitive to gcc --enable-threading= setting */
+ /*
{"multi-threaded",
"support multiple threads",
set_option, 1, &__mf_opts.multi_threaded},
+ */
{"wipe-stack",
"wipe stack objects at unwind",
set_option, 1, &__mf_opts.wipe_stack},
@@ -242,7 +252,7 @@
fprintf (stderr,
"This is a GCC \"mudflap\" memory-checked binary.\n"
- "Mudflap is Copyright (C) 2002 Free Software Foundation, Inc.\n"
+ "Mudflap is Copyright (C) 2002-2003 Free Software Foundation, Inc.\n"
"\n"
"The mudflap code can be controlled by an environment variable:\n"
"\n"
@@ -311,8 +321,7 @@
strncmp (optstr, "help", 4) == 0)
{
/* Caller will print help and exit. */
- rc = -1;
- break;
+ return -1;
}
if (strncmp (optstr, "no-", 3) == 0)
@@ -376,6 +385,9 @@
TRACE ("mf: set options from `%s'\n", saved_optstr);
+ /* Call this unconditionally, in case -sigusr1-report was toggled. */
+ __mf_sigusr1_respond ();
+
return rc;
}
@@ -435,6 +447,8 @@
static unsigned long __mf_count_unregister;
static unsigned long __mf_total_unregister_size;
static unsigned long __mf_count_violation [__MF_VIOL_WATCH+1];
+static unsigned long __mf_sigusr1_received;
+static unsigned long __mf_sigusr1_handled;
/* ------------------------------------------------------------------------ */
@@ -487,7 +501,7 @@
static void __mf_unlink_object (__mf_object_tree_t *obj);
static void __mf_describe_object (__mf_object_t *obj);
static unsigned __mf_watch_or_not (void *ptr, size_t sz, char flag);
-
+static void __mf_sigusr1_respond ();
/* ------------------------------------------------------------------------ */
@@ -570,9 +584,12 @@
uintptr_t ptr_high = CLAMPSZ (ptr, sz);
struct __mf_cache old_entry = *entry;
+ if (UNLIKELY (__mf_opts.sigusr1_report))
+ __mf_sigusr1_respond ();
+
BEGIN_RECURSION_PROTECT;
- TRACE ("mf: check ptr=%08lx size=%lu %s location=`%s'\n",
- ptr, sz, (type == 0 ? "read" : "write"), location);
+ TRACE ("mf: check ptr=%08lx b=%u size=%lu %s location=`%s'\n",
+ ptr, entry_idx, sz, (type == 0 ? "read" : "write"), location);
switch (__mf_opts.mudflap_mode)
{
@@ -816,7 +833,9 @@
new_obj->data.type = type;
new_obj->data.name = name;
new_obj->data.alloc_pc = pc;
+#if HAVE_GETTIMEOFDAY
gettimeofday (& new_obj->data.alloc_time, NULL);
+#endif
if (__mf_opts.backtrace > 0 && (type == __MF_TYPE_HEAP || type == __MF_TYPE_HEAP_I))
new_obj->data.alloc_backtrace_size =
@@ -874,6 +893,10 @@
type] += sz;
}
+
+ if (UNLIKELY (__mf_opts.sigusr1_report))
+ __mf_sigusr1_respond ();
+
switch (__mf_opts.mudflap_mode)
{
case mode_nop:
@@ -1050,8 +1073,11 @@
__mf_unregister (void *ptr, size_t sz)
{
DECLARE (void, free, void *ptr);
- BEGIN_RECURSION_PROTECT;
+ if (UNLIKELY (__mf_opts.sigusr1_report))
+ __mf_sigusr1_respond ();
+
+ BEGIN_RECURSION_PROTECT;
TRACE ("mf: unregister ptr=%08lx size=%lu\n", ptr, sz);
switch (__mf_opts.mudflap_mode)
@@ -1120,8 +1146,10 @@
old_obj->data.deallocated_p = 1;
old_obj->left = old_obj->right = NULL;
old_obj->data.dealloc_pc = (uintptr_t) __builtin_return_address (0);
+#if HAVE_GETTIMEOFDAY
gettimeofday (& old_obj->data.dealloc_time, NULL);
-
+#endif
+
if (__mf_opts.backtrace > 0 && old_obj->data.type == __MF_TYPE_HEAP)
old_obj->data.dealloc_backtrace_size =
__mf_backtrace (& old_obj->data.dealloc_backtrace,
@@ -1265,10 +1293,11 @@
struct tree_stats
{
unsigned obj_count;
- uintptr_t total_size;
+ unsigned long total_size;
unsigned live_obj_count;
double total_weight;
double weighted_size;
+ unsigned long weighted_address_bits [sizeof (uintptr_t) * 8][2];
};
@@ -1277,6 +1306,9 @@
{
assert (obj != NULL);
+ if (obj->left)
+ __mf_tree_analyze (obj->left, s);
+
/* Exclude never-accessed objects. */
if (obj->data.read_count + obj->data.write_count)
{
@@ -1285,16 +1317,28 @@
if (obj->data.liveness)
{
+ unsigned i;
+ uintptr_t addr;
+
+ VERBOSE_TRACE ("mf: analyze low=%08lx live=%u name=`%s'\n",
+ obj->data.low, obj->data.liveness, obj->data.name);
+
s->live_obj_count ++;
s->total_weight += (double) obj->data.liveness;
s->weighted_size +=
(double) (obj->data.high - obj->data.low + 1) *
(double) obj->data.liveness;
+
+ addr = obj->data.low;
+ for (i=0; i<sizeof(uintptr_t) * 8; i++)
+ {
+ unsigned bit = addr & 1;
+ s->weighted_address_bits[i][bit] += obj->data.liveness;
+ addr = addr >> 1;
+ }
}
}
- if (obj->left)
- __mf_tree_analyze (obj->left, s);
if (obj->right)
__mf_tree_analyze (obj->right, s);
}
@@ -1304,13 +1348,11 @@
__mf_adapt_cache ()
{
struct tree_stats s;
- double avg_weight;
- uintptr_t avg_size;
- uintptr_t weighted_avg_size;
uintptr_t new_mask;
- uintptr_t shifted;
unsigned char new_shift;
- double cache_utilization;
+ float cache_utilization;
+ float max_value;
+ static float smoothed_new_shift = -1.0;
unsigned i;
memset (&s, 0, sizeof (s));
@@ -1323,10 +1365,25 @@
if (! (s.obj_count > 0) && (s.live_obj_count > 0) && (s.total_weight > 0.0))
return;
- avg_weight = s.total_weight / s.live_obj_count;
- weighted_avg_size = (uintptr_t) (s.weighted_size / s.total_weight);
- avg_size = (uintptr_t) (s.total_size / s.obj_count);
- if (avg_size == 0) avg_size = 1;
+ /* Guess a good value for the shift parameter by finding an address bit that is a
+ good discriminant of lively objects. */
+ max_value = 0.0;
+ for (i=0; i<sizeof (uintptr_t)*8; i++)
+ {
+ float value = (float) s.weighted_address_bits[i][0] * (float) s.weighted_address_bits[i][1];
+ if (max_value < value) max_value = value;
+ }
+ for (i=0; i<sizeof (uintptr_t)*8; i++)
+ {
+ float shoulder_factor = 0.7; /* Include slightly less popular bits too. */
+ float value = (float) s.weighted_address_bits[i][0] * (float) s.weighted_address_bits[i][1];
+ if (value >= max_value * shoulder_factor)
+ break;
+ }
+ if (smoothed_new_shift < 0) smoothed_new_shift = __mf_lc_shift;
+ /* Converge toward this slowly to reduce flapping. */ smoothed_new_shift = 0.9*smoothed_new_shift + 0.1*i;
+ new_shift = (unsigned) (smoothed_new_shift + 0.5);
+ assert (new_shift >= 0 && new_shift < sizeof (uintptr_t)*8);
/* Count number of used buckets. */
cache_utilization = 0.0;
@@ -1334,37 +1391,14 @@
if (__mf_lookup_cache[i].low != 0 || __mf_lookup_cache[i].high != 0)
cache_utilization += 1.0;
cache_utilization /= (1 + __mf_lc_mask);
- if (cache_utilization < 0.5)
- new_mask = __mf_lc_mask >> 1;
- else if (cache_utilization > 0.7)
- new_mask = (__mf_lc_mask << 1) | 1;
- else
- new_mask = __mf_lc_mask;
-
- new_mask |= 0x3ff; /* impose a practical minimum */
+
+ new_mask |= 0x3ff; /* XXX: force a large cache. */
new_mask &= (LOOKUP_CACHE_SIZE_MAX - 1);
-#if 0 /* giving up on heuristics? */
- new_mask = __mf_lc_mask;
-#endif
-
- /* Find a good new shift amount. Make it big enough that the
- popular objects take up 1-2 "cache lines". The 24 in the
- next line imposes a practical 16MB upper limit on the cache line
- size. */
- for (new_shift=0, shifted=2; /* shifted == 2**(new_shift+1) */
- new_shift < 24;
- new_shift++, shifted<<=1)
- if (shifted > weighted_avg_size)
- break;
-
- VERBOSE_TRACE ("mf: adapt cache %u/%u/%lu/%.0f/%.0f => "
- "%.0f/%lu/%lu/%.0f%% => "
- "%08lx/%u\n",
- s.obj_count, s.live_obj_count, s.total_size,
- s.total_weight, s.weighted_size,
- avg_weight, weighted_avg_size, avg_size, (cache_utilization*100.0),
- new_mask, new_shift);
+ VERBOSE_TRACE ("mf: adapt cache obj=%u/%u sizes=%lu/%.0f/%.0f => "
+ "util=%u%% m=%08lx s=%u\n",
+ s.obj_count, s.live_obj_count, s.total_size, s.total_weight, s.weighted_size,
+ (unsigned)(cache_utilization*100.0), new_mask, new_shift);
/* We should reinitialize cache if its parameters have changed. */
if (new_mask != __mf_lc_mask ||
@@ -1684,13 +1718,19 @@
static unsigned
__mf_report_leaks (__mf_object_tree_t *node)
{
- static unsigned count = 0; /* Shared amongst recursive calls. */
- /* XXX: when to reset? */
+ /* The counter is amongst recursive calls, so
+ that cumulative numbers are printed below. */
+ static unsigned count = 0;
- if (node == NULL) return count;
+ if (node == NULL) /* Reset */
+ {
+ count = 0;
+ return 0;
+ }
/* Inorder traversal. */
- __mf_report_leaks (node->left);
+ if (node->left)
+ __mf_report_leaks (node->left);
if (node->data.type == __MF_TYPE_HEAP
|| node->data.type == __MF_TYPE_HEAP_I)
{
@@ -1698,7 +1738,8 @@
fprintf (stderr, "Leaked object %u:\n", count);
__mf_describe_object (& node->data);
}
- __mf_report_leaks (node->right);
+ if (node->right)
+ __mf_report_leaks (node->right);
return count;
}
@@ -1775,6 +1816,7 @@
/* Free up any remaining alloca()'d blocks. */
(void) CALL_WRAP (alloca, 0); /* XXX: doesn't work in shared mode. */
__mf_describe_object (NULL); /* Reset description epoch. */
+ __mf_report_leaks (NULL); /* Reset cumulative count. */
l = __mf_report_leaks (__mf_object_root);
fprintf (stderr, "number of leaked objects: %u\n", l);
}
@@ -1895,7 +1937,9 @@
unsigned dead_p;
unsigned num_helpful = 0;
struct timeval now;
+#if HAVE_GETTIMEOFDAY
gettimeofday (& now, NULL);
+#endif
violation_number ++;
fprintf (stderr,
@@ -2098,4 +2142,41 @@
TRACE (" hits=%u\n", count);
return count;
+}
+
+
+void
+__mf_sigusr1_handler (int num)
+{
+ __mf_sigusr1_received ++;
+}
+
+/* Install or remove SIGUSR1 handler as necessary.
+ Also, respond to a received pending SIGUSR1. */
+void
+__mf_sigusr1_respond ()
+{
+ static int handler_installed;
+
+#if HAVE_SIGNAL
+ /* Manage handler */
+ if (__mf_opts.sigusr1_report && ! handler_installed)
+ {
+ signal (SIGUSR1, __mf_sigusr1_handler);
+ handler_installed = 1;
+ }
+ else if(! __mf_opts.sigusr1_report && handler_installed)
+ {
+ signal (SIGUSR1, SIG_DFL);
+ handler_installed = 0;
+ }
+#endif
+
+ /* Manage enqueued signals */
+ if (__mf_sigusr1_received > __mf_sigusr1_handled)
+ {
+ __mf_sigusr1_handled ++;
+ __mf_report ();
+ handler_installed = 0; /* We may need to re-enable signal; this might be a SysV library. */
+ }
}
Index: testsuite/libmudflap.c/fail27-frag.c
===================================================================
RCS file: testsuite/libmudflap.c/fail27-frag.c
diff -N testsuite/libmudflap.c/fail27-frag.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ testsuite/libmudflap.c/fail27-frag.c 28 Mar 2003 22:26:38 -0000
@@ -0,0 +1,21 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+char *foo (unsigned i)
+{
+ char buffer[10];
+ char *k = i ? & buffer[i] : NULL; /* defeat addr-of-local-returned warning */
+ return k;
+}
+
+int main ()
+{
+char *f = foo (5);
+f[0] = 'b';
+
+return 0;
+}
+/* { dg-output "mudflap violation 1.*" } */
+/* { dg-output "Nearby object.*" } */
+/* { dg-output "mudflap object.*buffer.*alloc.*dealloc" } */
+/* { dg-do run { xfail *-*-* } } */
Index: testsuite/libmudflap.c/pass36-frag.c
===================================================================
RCS file: testsuite/libmudflap.c/pass36-frag.c
diff -N testsuite/libmudflap.c/pass36-frag.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ testsuite/libmudflap.c/pass36-frag.c 28 Mar 2003 22:26:38 -0000
@@ -0,0 +1,14 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+
+int main ()
+{
+char *k;
+__mf_set_options ("-sigusr1-report -print-leaks");
+k = (char *) malloc (100);
+raise (SIGUSR1);
+return 0;
+}
+/* { dg-output "Leaked object.*name=.malloc region.*objects: 1" } */