drd: Add __cxa_guard_*() intercepts

git-svn-id: svn://svn.valgrind.org/valgrind/trunk@14012 a5019735-40e9-0310-863c-91ae7b9d1cf9
diff --git a/drd/Makefile.am b/drd/Makefile.am
index 153b384..4ead4ec 100644
--- a/drd/Makefile.am
+++ b/drd/Makefile.am
@@ -122,6 +122,7 @@
 endif
 
 VGPRELOAD_DRD_SOURCES_COMMON = \
+  drd_libstdcxx_intercepts.c   \
   drd_pthread_intercepts.c     \
   drd_qtcore_intercepts.c      \
   drd_strmem_intercepts.c
diff --git a/drd/drd_clientreq.h b/drd/drd_clientreq.h
index c5d0623..929b60d 100644
--- a/drd/drd_clientreq.h
+++ b/drd/drd_clientreq.h
@@ -240,6 +240,7 @@
    mutex_type_errorcheck_mutex = 2,
    mutex_type_default_mutex    = 3,
    mutex_type_spinlock         = 4,
+   mutex_type_cxa_guard        = 5,
 } MutexT;
 
 /**
diff --git a/drd/drd_libstdcxx_intercepts.c b/drd/drd_libstdcxx_intercepts.c
new file mode 100644
index 0000000..99cbd3f
--- /dev/null
+++ b/drd/drd_libstdcxx_intercepts.c
@@ -0,0 +1,109 @@
+/*--------------------------------------------------------------------*/
+/*--- Client-space code for DRD.        drd_libstdcxx_intercepts.c ---*/
+/*--------------------------------------------------------------------*/
+
+/*
+  This file is part of DRD, a thread error detector.
+
+  Copyright (C) 2014 Bart Van Assche <bvanassche@acm.org>.
+
+  This program is free software; you can redistribute it and/or
+  modify it under the terms of the GNU General Public License as
+  published by the Free Software Foundation; either version 2 of the
+  License, or (at your option) any later version.
+
+  This program 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
+  General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+  02111-1307, USA.
+
+  The GNU General Public License is contained in the file COPYING.
+*/
+
+/* ---------------------------------------------------------------------
+   ALL THE CODE IN THIS FILE RUNS ON THE SIMULATED CPU.
+
+   These functions are not called directly - they're the targets of code
+   redirection or load notifications (see pub_core_redir.h for info).
+   They're named weirdly so that the intercept code can find them when the
+   shared object is initially loaded.
+
+   Note that this filename has the "drd_" prefix because it can appear
+   in stack traces, and the "drd_" makes it a little clearer that it
+   originates from Valgrind.
+   ------------------------------------------------------------------ */
+
+#include "drd_basics.h"     /* DRD_() */
+#include "drd_clientreq.h"
+#include "pub_tool_redir.h" /* VG_WRAP_FUNCTION_ZZ() */
+
+/* From <cxxabi.h> */
+int __cxa_guard_acquire(void* guard);
+void __cxa_guard_release(void* guard) __attribute__((__nothrow__));
+void __cxa_guard_abort(void* guard) __attribute__((__nothrow__));
+
+#define LIBSTDCXX_FUNC(ret_ty, zf, implf, argl_decl, argl)             \
+   ret_ty VG_WRAP_FUNCTION_ZZ(VG_Z_LIBSTDCXX_SONAME,zf) argl_decl;     \
+   ret_ty VG_WRAP_FUNCTION_ZZ(VG_Z_LIBSTDCXX_SONAME,zf) argl_decl      \
+   { return implf argl; }
+
+/*
+ * Not inlining one of the intercept functions will cause the regression
+ * tests to fail because this would cause an additional stackfram to appear
+ * in the output. The __always_inline macro guarantees that inlining will
+ * happen, even when compiling with optimization disabled.
+ */
+#undef __always_inline /* since already defined in <cdefs.h> */
+#if __GNUC__ > 3 || __GNUC__ == 3 && __GNUC_MINOR__ >= 2
+#define __always_inline __inline__ __attribute__((always_inline))
+#else
+#define __always_inline __inline__
+#endif
+
+static __always_inline
+int __cxa_guard_acquire_intercept(void *guard)
+{
+   int   ret;
+   OrigFn fn;
+   VALGRIND_GET_ORIG_FN(fn);
+   VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__PRE_MUTEX_LOCK,
+                                   guard, mutex_type_cxa_guard, 0, 0, 0);
+   CALL_FN_W_W(ret, fn, guard);
+   VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__POST_MUTEX_LOCK,
+                                   guard, 1, 0, 0, 0);
+   if (ret == 0) {
+      VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__PRE_MUTEX_UNLOCK,
+                                      guard, mutex_type_cxa_guard, 0, 0, 0);
+      VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__POST_MUTEX_UNLOCK,
+                                      guard, 0, 0, 0, 0);
+   }
+   return ret;
+}
+
+LIBSTDCXX_FUNC(int, ZuZucxaZuguardZuacquire, __cxa_guard_acquire_intercept,
+               (void *guard), (guard));
+LIBSTDCXX_FUNC(int, ZuZucxaZuguardZuacquireZAZACXXABIZu1Zd3,
+               __cxa_guard_acquire_intercept, (void *guard), (guard));
+
+static __always_inline
+void __cxa_guard_abort_release_intercept(void *guard)
+{
+   int ret;
+   OrigFn fn;
+   VALGRIND_GET_ORIG_FN(fn);
+   VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__PRE_MUTEX_UNLOCK,
+                                   guard, mutex_type_cxa_guard, 0, 0, 0);
+   CALL_FN_W_W(ret, fn, guard);
+   VALGRIND_DO_CLIENT_REQUEST_STMT(VG_USERREQ__POST_MUTEX_UNLOCK,
+                                   guard, 0, 0, 0, 0);
+}
+
+LIBSTDCXX_FUNC(void, ZuZucxaZuguardZurelease,
+               __cxa_guard_abort_release_intercept, (void *guard), (guard));
+LIBSTDCXX_FUNC(void, ZuZucxaZuguardZuabort,
+               __cxa_guard_abort_release_intercept, (void *guard), (guard));
diff --git a/drd/drd_mutex.c b/drd/drd_mutex.c
index 4c836fd..12ee4de 100644
--- a/drd/drd_mutex.c
+++ b/drd/drd_mutex.c
@@ -468,6 +468,8 @@
       return "mutex";
    case mutex_type_spinlock:
       return "spinlock";
+   case mutex_type_cxa_guard:
+      return "cxa_guard";
    }
    tl_assert(0);
    return "?";