[glib] Initial support for dtrace and systemtap



commit bef9efd0a99a9a3bd6a2d713423edc37d6a38f21
Author: Alexander Larsson <alexl redhat com>
Date:   Fri Dec 18 21:25:47 2009 +0100

    Initial support for dtrace and systemtap
    
    This adds static markers for dtrace, which are also usable
    by systemtap. Additionally it adds a tapset for systemtap
    that makes it easier to use the static markers.
    
    These are enabled by default.
    
    This initial set of probes is rather limited:
    
    * allocation and free using g_malloc & co
    * allocation and free using g_slice
    * gquark name tracking (useful for converting quarks to strings in probes)
    
    Notes on naming:
    
    Its traditional with dtrace to use probe names with dashes as
    delimiter (slice-alloc). Since dashes are not usable in identifiers
    the C code uses double underscores (slice__alloc) which is converted
    to dashes in the UI. We follow this for the shared lowlevel probe
    names.
    
    Additionally dtrace supports putting a "provider" part in the probe
    names which is essentially a namespacing thing. On systemtap this
    field is currently ignored (but may be implemented in the future), but
    this is not really a problem since in systemtap the probes are
    specified by combining the solib file and the marker name, so there
    can't really be name conflicts.
    
    For the systemtap tapset highlevel probes we instead use names that
    are systemtapish with single dashes as separators.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=606044

 configure.in                      |   53 +++++++++++++++++++++++
 docs/reference/glib/building.sgml |   42 ++++++++++++++++++
 docs/reference/glib/running.sgml  |   13 ++++++
 glib/Makefile.am                  |   23 ++++++++++
 glib/gdataset.c                   |    6 ++-
 glib/glib.stp.in                  |   84 +++++++++++++++++++++++++++++++++++++
 glib/glib_probes.d                |    8 ++++
 glib/glib_trace.h                 |   43 +++++++++++++++++++
 glib/gmem.c                       |   52 ++++++++++++++++++-----
 glib/gslice.c                     |    5 ++
 10 files changed, 317 insertions(+), 12 deletions(-)
---
diff --git a/configure.in b/configure.in
index 6cc3bc9..17214d9 100644
--- a/configure.in
+++ b/configure.in
@@ -2773,6 +2773,58 @@ fi
 
 AM_CONDITIONAL(ENABLE_MAN, test x$enable_man != xno)
 
+dnl
+dnl Tracing
+dnl
+
+AC_ARG_ENABLE([dtrace],
+             [AS_HELP_STRING([--enable-dtrace],
+                              [Enable inclusion of dtrace trace support])])
+have_dtrace=no
+AC_MSG_CHECKING([whether to include dtrace tracing support])
+if test "x$enable_dtrace" != xno; then
+  AC_MSG_RESULT([yes])
+  AC_CHECK_PROGS(DTRACE, dtrace)
+  if test -z "$DTRACE"; then
+    if test "x$enable_dtrace" = xyes; then
+      AC_MSG_ERROR([dtrace not found])
+    fi
+  fi
+  AC_CHECK_HEADER([sys/sdt.h],have_dtrace=yes,
+                  [if test "x$enable_dtrace" = xyes; then
+                     AC_MSG_ERROR([dtrace support needs sys/sdt.h header])
+                   fi])
+else
+  AC_MSG_RESULT([no])
+fi
+if test "x$have_dtrace" = xyes; then
+  AC_DEFINE([HAVE_DTRACE], [1], [Define to 1 if using dtrace probes.])
+fi
+AM_CONDITIONAL([ENABLE_DTRACE], [test x$have_dtrace = xyes ])
+
+AC_MSG_CHECKING([whether to include systemtap tracing support])
+AC_ARG_ENABLE([systemtap],
+             [AS_HELP_STRING([--enable-systemtap],
+                              [Enable inclusion of systemtap trace support])])
+have_systemtap=no
+if test "x$enable_systemtap" != xno -a "x$have_dtrace" = xyes; then
+  have_systemtap=yes
+fi
+AC_MSG_RESULT(${have_systemtap})
+
+AM_CONDITIONAL([ENABLE_SYSTEMTAP], [test x$have_systemtap = xyes])
+
+AC_ARG_WITH([tapset-install-dir],
+            [AS_HELP_STRING([--with-tapset-install-dir],
+            [The absolute path where the systemtap tapsets will be installed])],
+            [if test "x${withval}" = x; then
+	       ABS_TAPSET_DIR="\$(datadir)/systemtap/tapset"
+	     else
+	       ABS_TAPSET_DIR="${withval}"
+	      fi],
+	    [ABS_TAPSET_DIR="\$(datadir)/systemtap/tapset"])
+AC_SUBST(ABS_TAPSET_DIR)
+
 dnl ******************************
 dnl *** output the whole stuff ***
 dnl ******************************
@@ -3531,6 +3583,7 @@ build/win32/Makefile
 build/win32/dirent/Makefile
 build/win32/vs9/Makefile
 glib/Makefile
+glib/glib.stp
 glib/libcharset/Makefile
 glib/gnulib/Makefile
 glib/pcre/Makefile
diff --git a/docs/reference/glib/building.sgml b/docs/reference/glib/building.sgml
index a49a4d6..806349e 100644
--- a/docs/reference/glib/building.sgml
+++ b/docs/reference/glib/building.sgml
@@ -176,6 +176,19 @@ How to compile GLib itself
           <option>--disable-selinux</option> configure option.
         </para>
       </listitem>
+      <listitem>
+        <para>
+          The optional support for DTrace requires the <filename>sys/sdt.h</filename> header,
+          which is provided by SystemTap on Linux.  To build GLib without DTrace, use the
+          <option>--disable-dtrace</option> configure option.
+        </para>
+      </listitem>
+      <listitem>
+        <para>
+          The optional support for <ulink url="http://sourceware.org/systemtap/";>SystemTap</ulink> can be disabled with the
+          <option>--disable-systemtap</option> configure option.
+        </para>
+      </listitem>
     </itemizedlist>
 
     </refsect1>
@@ -239,6 +252,14 @@ How to compile GLib itself
             <arg>--enable-selinux</arg>
           </group>
           <group>
+            <arg>--disable-dtrace</arg>
+            <arg>--enable-dtrace</arg>
+          </group>
+            <group>
+            <arg>--disable-systemtap</arg>
+            <arg>--enable-systemtap</arg>
+          </group>
+          <group>
             <arg>--with-runtime-libdir=RELPATH</arg>
           </group>
         </cmdsynopsis>
@@ -562,6 +583,27 @@ How to compile GLib itself
 	  be included.
         </para>
      </formalpara>
+     
+     <formalpara>
+        <title><systemitem>--disable-dtrace</systemitem> and
+          <systemitem>--enable-dtrace</systemitem></title>
+
+        <para>
+          By default the <command>configure</command> script will
+          detect if DTrace support is available, and use it.
+        </para>
+     </formalpara>
+
+     <formalpara>
+        <title><systemitem>--disable-systemtap</systemitem> and
+          <systemitem>--enable-systemtap</systemitem></title>
+
+        <para>
+          This option requires DTrace support.  If it is available, then
+	  the <command>configure</command> script will also check for
+	  the presence of SystemTap.
+        </para>
+     </formalpara>
 
       <formalpara>
         <title><systemitem>--with-runtime-libdir=RELPATH</systemitem></title>
diff --git a/docs/reference/glib/running.sgml b/docs/reference/glib/running.sgml
index 9c33da8..ea10d15 100644
--- a/docs/reference/glib/running.sgml
+++ b/docs/reference/glib/running.sgml
@@ -306,6 +306,19 @@ Which would print the contents of each widget in a list of widgets.
 </refsect2>
 
 <refsect2>
+<title>SystemTap</title>
+
+<para>
+<ulink url="http://sourceware.org/systemtap/";>SystemTap</ulink> is a dynamic whole-system
+analysis toolkit.  GLib ships with a file <filename>glib.stp</filename> which defines a
+set of probe points, which you can hook into with custom SystemTap scripts.
+See the files <filename>glib.stp</filename> and <filename>gobject.stp</filename> which
+are in your shared SystemTap scripts directory.
+</para>
+
+</refsect2>
+
+<refsect2>
 <title>Memory statistics</title>
 
 <para>
diff --git a/glib/Makefile.am b/glib/Makefile.am
index 8882ae2..7eac7ff 100644
--- a/glib/Makefile.am
+++ b/glib/Makefile.am
@@ -1,6 +1,8 @@
 ## Process this file with automake to produce Makefile.in
 include $(top_srcdir)/Makefile.decl
 
+CLEANFILES=
+
 if HAVE_GOOD_PRINTF
 else
 PRINTF_SUBDIR = gnulib
@@ -104,6 +106,7 @@ uninstall-ms-lib:
 	$(uninstall_ms_lib_cmd)
 
 libglib_2_0_la_SOURCES = 	\
+	glib_probes.d		\
 	garray.c		\
 	gasyncqueue.c		\
 	$(gatomic_c)		\
@@ -130,6 +133,7 @@ libglib_2_0_la_SOURCES = 	\
 	giochannel.c    	\
 	gkeyfile.c        	\
 	glibintl.h		\
+	glib_trace.h		\
 	glist.c			\
 	gmain.c         	\
 	gmappedfile.c		\
@@ -325,6 +329,25 @@ libglib_2_0_la_LDFLAGS = \
 
 INSTALL_PROGS=
 
+
+if ENABLE_DTRACE
+glib_probes.h: glib_probes.d Makefile
+	$(DTRACE) -C -h -s $< -o $  tmp
+	sed -e "s,define STAP_HAS_SEMAPHORES 1,undef STAP_HAS_SEMAPHORES," < $  tmp > $@ && rm -f $  tmp
+glib_probes.o: glib_probes.d Makefile
+	$(DTRACE) -G -s $< -o $@
+BUILT_SOURCES += glib_probes.h glib_probes.o
+CLEANFILES += glib_probes.h glib_probes.h.tmp
+libglib_2_0_la_LIBADD += glib_probes.o
+endif
+
+if ENABLE_SYSTEMTAP
+tapset_in_files = glib.stp.in
+tapsetdir   = $(DESTDIR)@ABS_TAPSET_DIR@
+tapset_DATA = $(tapset_in_files:.stp.in=.stp)
+EXTRA_DIST += $(tapset_in_files)
+endif
+
 gspawn-win32-helper-console.c:
 	echo '#define HELPER_CONSOLE' >$@
 	echo '#include "gspawn-win32-helper.c"' >>$@
diff --git a/glib/gdataset.c b/glib/gdataset.c
index 60adfe4..57f6523 100644
--- a/glib/gdataset.c
+++ b/glib/gdataset.c
@@ -36,6 +36,7 @@
 
 #include "glib.h"
 #include "gdatasetprivate.h"
+#include "glib_trace.h"
 #include "galias.h"
 
 /**
@@ -1047,7 +1048,10 @@ g_quark_from_string_internal (const gchar *string,
     quark = GPOINTER_TO_UINT (g_hash_table_lookup (g_quark_ht, string));
   
   if (!quark)
-    quark = g_quark_new (duplicate ? quark_strdup (string) : (gchar *)string);
+    {
+      quark = g_quark_new (duplicate ? quark_strdup (string) : (gchar *)string);
+      TRACE(GLIB_QUARK_NEW(string, quark));
+    }
 
   return quark;
 }
diff --git a/glib/glib.stp.in b/glib/glib.stp.in
new file mode 100644
index 0000000..95d3351
--- /dev/null
+++ b/glib/glib.stp.in
@@ -0,0 +1,84 @@
+global gquarks
+
+/* This is needed to keep track of gquark for use in other probes.*/
+probe process("@ABS_GLIB_RUNTIME_LIBDIR@/libglib-2 0 so 0  LT_CURRENT@  LT_REVISION@").mark("quark__new")
+{
+  gquarks[pid(), $arg2] = user_string($arg1)
+}
+
+/**
+ * probe glib.quark_new - Called when a #GQuark is initially created
+ * @quark: integer value for the quark
+ * @str: string form of the quark
+ */
+probe glib.quark_new = process("@ABS_GLIB_RUNTIME_LIBDIR@/libglib-2 0 so 0  LT_CURRENT@  LT_REVISION@").mark("quark__new")
+{
+  str = user_string ($arg1); 
+  quark = $arg2; 
+  probestr = sprintf("glib.quark_new(%s) -> %d", str, quark);
+}
+
+/**
+ * probe glib.mem_alloc - Called when a malloc block is initially requested
+ * @mem: Raw memory pointer returned
+ * @n_bytes: number of bytes
+ * @zeroed: Boolean value, %TRUE if this block was filled with NUL bytes
+ * @failable: Boolean value, %TRUE if program execution can continue on allocation failure
+ */
+probe glib.mem_alloc = process("@ABS_GLIB_RUNTIME_LIBDIR@/libglib-2 0 so 0  LT_CURRENT@  LT_REVISION@").mark("mem__alloc")
+{
+  mem = $arg1; 
+  n_bytes = $arg2; 
+  zeroed = $arg3; 
+  failable = $arg4; 
+  probestr = sprintf("glib.mem_alloc(n_bytes=%d) -> %p", n_bytes, mem);
+}
+
+/**
+ * probe glib.mem_free - Called when a malloc block freed
+ */
+probe glib.mem_free = process("@ABS_GLIB_RUNTIME_LIBDIR@/libglib-2 0 so 0  LT_CURRENT@  LT_REVISION@").mark("mem__free")
+{
+  mem = $arg1;  /* ARG: @mem: Raw memory pointer */
+  probestr = sprintf("glib.mem_free(mem=%p)", mem);
+}
+
+/**
+ * probe glib.mem_realloc - Called when a malloc block is resized
+ * @mem: Raw memory pointer returned
+ * @old_mem: Original memory pointer
+ * @n_bytes: number of bytes
+ * @failable: Boolean value, %TRUE if program execution can continue on allocation failure
+ */
+probe glib.mem_realloc = process("@ABS_GLIB_RUNTIME_LIBDIR@/libglib-2 0 so 0  LT_CURRENT@  LT_REVISION@").mark("mem__realloc")
+{
+  mem = $arg1; 
+  old_mem = $arg2; 
+  n_bytes = $arg3;  
+  failable = $arg4; 
+  probestr = sprintf("glib.mem_realloc(old_mem=%p, n_bytes=%d) -> %p", old_mem, n_bytes, mem);
+}
+
+/**
+ * probe glib.slice_alloc - Called when g_slice_alloc() is used
+ * @mem: Raw memory pointer returned
+ * @n_bytes: number of bytes
+ */
+probe glib.slice_alloc = process("@ABS_GLIB_RUNTIME_LIBDIR@/libglib-2 0 so 0  LT_CURRENT@  LT_REVISION@").mark("slice__alloc")
+{
+  mem = $arg1; 
+  n_bytes = $arg2; 
+  probestr = sprintf("glib.slice_alloc(n_bytes=%d) -> %p", n_bytes, mem);
+}
+
+/**
+ * probe glib.slice_free - Called when memory slice is freed
+ * @mem: Raw memory pointer returned
+ * @n_bytes: Number of bytes
+ */
+probe glib.slice_free = process("@ABS_GLIB_RUNTIME_LIBDIR@/libglib-2 0 so 0  LT_CURRENT@  LT_REVISION@").mark("slice__free")
+{
+  mem = $arg1; 
+  n_bytes = $arg2; 
+  probestr = sprintf("glib.slice_free(n_bytes=%d) -> %p", n_bytes, mem);
+}
diff --git a/glib/glib_probes.d b/glib/glib_probes.d
new file mode 100644
index 0000000..9232e1b
--- /dev/null
+++ b/glib/glib_probes.d
@@ -0,0 +1,8 @@
+provider glib {
+	probe mem__alloc(void*, unsigned int, unsigned int, unsigned int);
+	probe mem__realloc(void*, void *, unsigned int, unsigned int);
+	probe mem__free(void*);
+	probe slice__alloc(void*, unsigned int);
+	probe slice__free(void*, unsigned int);
+	probe quark__new(char *, unsigned int);
+};
diff --git a/glib/glib_trace.h b/glib/glib_trace.h
new file mode 100644
index 0000000..789e88d
--- /dev/null
+++ b/glib/glib_trace.h
@@ -0,0 +1,43 @@
+/* GLIB - Library of useful routines for C programming
+ * 
+ * Copyright (C) 2009,2010 Red Hat, Inc.
+ *
+ * This 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 of the License, or (at your option) any later version.
+ *
+ * This 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 this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: Alexander Larsson <alexl redhat com>
+ */
+
+#ifndef __GLIBTRACE_H__
+#define __GLIBTRACE_H__
+
+#ifndef SIZEOF_CHAR
+#error "config.h must be included prior to glib_trace.h"
+#endif
+
+#ifdef HAVE_DTRACE
+
+/* include the generated probes header and put markers in code */
+#include "glib_probes.h"
+#define TRACE(probe) probe
+
+#else
+
+/* Wrap the probe to allow it to be removed when no systemtap available */
+#define TRACE(probe)
+
+#endif
+
+#endif /* __GLIBTRACE_H__ */
diff --git a/glib/gmem.c b/glib/gmem.c
index b468def..f62e604 100644
--- a/glib/gmem.c
+++ b/glib/gmem.c
@@ -39,6 +39,7 @@
 #include "gbacktrace.h"
 #include "gtestutils.h"
 #include "gthread.h"
+#include "glib_trace.h"
 
 #include "galias.h"
 
@@ -121,7 +122,6 @@ static GMemVTable glib_mem_vtable = {
   standard_try_realloc,
 };
 
-
 /* --- functions --- */
 gpointer
 g_malloc (gsize n_bytes)
@@ -133,6 +133,7 @@ g_malloc (gsize n_bytes)
       gpointer mem;
 
       mem = glib_mem_vtable.malloc (n_bytes);
+      TRACE (GLIB_MEM_ALLOC((void*) mem, (unsigned int) n_bytes, 0, 0));
       if (mem)
 	return mem;
 
@@ -140,6 +141,8 @@ g_malloc (gsize n_bytes)
                G_STRLOC, n_bytes);
     }
 
+  TRACE(GLIB_MEM_ALLOC((void*) NULL, (int) n_bytes, 0, 0));
+
   return NULL;
 }
 
@@ -153,6 +156,7 @@ g_malloc0 (gsize n_bytes)
       gpointer mem;
 
       mem = glib_mem_vtable.calloc (1, n_bytes);
+      TRACE (GLIB_MEM_ALLOC((void*) mem, (unsigned int) n_bytes, 1, 0));
       if (mem)
 	return mem;
 
@@ -160,6 +164,8 @@ g_malloc0 (gsize n_bytes)
                G_STRLOC, n_bytes);
     }
 
+  TRACE(GLIB_MEM_ALLOC((void*) NULL, (int) n_bytes, 1, 0));
+
   return NULL;
 }
 
@@ -167,13 +173,16 @@ gpointer
 g_realloc (gpointer mem,
 	   gsize    n_bytes)
 {
+  gpointer newmem;
+
   if (G_UNLIKELY (!g_mem_initialized))
     g_mem_init_nomessage();
   if (G_LIKELY (n_bytes))
     {
-      mem = glib_mem_vtable.realloc (mem, n_bytes);
-      if (mem)
-	return mem;
+      newmem = glib_mem_vtable.realloc (mem, n_bytes);
+      TRACE (GLIB_MEM_REALLOC((void*) newmem, (void*)mem, (unsigned int) n_bytes, 0));
+      if (newmem)
+	return newmem;
 
       g_error ("%s: failed to allocate %"G_GSIZE_FORMAT" bytes",
                G_STRLOC, n_bytes);
@@ -182,6 +191,8 @@ g_realloc (gpointer mem,
   if (mem)
     glib_mem_vtable.free (mem);
 
+  TRACE (GLIB_MEM_REALLOC((void*) NULL, (void*)mem, 0, 0));
+
   return NULL;
 }
 
@@ -192,17 +203,24 @@ g_free (gpointer mem)
     g_mem_init_nomessage();
   if (G_LIKELY (mem))
     glib_mem_vtable.free (mem);
+  TRACE(GLIB_MEM_FREE((void*) mem));
 }
 
 gpointer
 g_try_malloc (gsize n_bytes)
 {
+  gpointer mem;
+
   if (G_UNLIKELY (!g_mem_initialized))
     g_mem_init_nomessage();
   if (G_LIKELY (n_bytes))
-    return glib_mem_vtable.try_malloc (n_bytes);
+    mem = glib_mem_vtable.try_malloc (n_bytes);
   else
-    return NULL;
+    mem = NULL;
+
+  TRACE (GLIB_MEM_ALLOC((void*) mem, (unsigned int) n_bytes, 0, 1));
+
+  return mem;
 }
 
 gpointer
@@ -210,7 +228,12 @@ g_try_malloc0 (gsize n_bytes)
 {
   gpointer mem;
 
-  mem = g_try_malloc (n_bytes);
+  if (G_UNLIKELY (!g_mem_initialized))
+    g_mem_init_nomessage();
+  if (G_LIKELY (n_bytes))
+    mem = glib_mem_vtable.try_malloc (n_bytes);
+  else
+    mem = NULL;
 
   if (mem)
     memset (mem, 0, n_bytes);
@@ -222,15 +245,22 @@ gpointer
 g_try_realloc (gpointer mem,
 	       gsize    n_bytes)
 {
+  gpointer newmem;
+
   if (G_UNLIKELY (!g_mem_initialized))
     g_mem_init_nomessage();
   if (G_LIKELY (n_bytes))
-    return glib_mem_vtable.try_realloc (mem, n_bytes);
+    newmem = glib_mem_vtable.try_realloc (mem, n_bytes);
+  else
+    {
+      newmem = NULL;
+      if (mem)
+	glib_mem_vtable.free (mem);
+    }
 
-  if (mem)
-    glib_mem_vtable.free (mem);
+  TRACE (GLIB_MEM_REALLOC((void*) newmem, (void*)mem, (unsigned int) n_bytes, 1));
 
-  return NULL;
+  return newmem;
 }
 
 
diff --git a/glib/gslice.c b/glib/gslice.c
index 66fd9e5..41bd93c 100644
--- a/glib/gslice.c
+++ b/glib/gslice.c
@@ -35,6 +35,7 @@
 #include "gmem.h"               /* gslice.h */
 #include "gthreadprivate.h"
 #include "glib.h"
+#include "glib_trace.h"
 #include "galias.h"
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>             /* sysconf() */
@@ -836,6 +837,9 @@ g_slice_alloc (gsize mem_size)
     mem = g_malloc (mem_size);
   if (G_UNLIKELY (allocator->config.debug_blocks))
     smc_notify_alloc (mem, mem_size);
+
+  TRACE (GLIB_SLICE_ALLOC((void*)mem, mem_size));
+
   return mem;
 }
 
@@ -897,6 +901,7 @@ g_slice_free1 (gsize    mem_size,
         memset (mem_block, 0, mem_size);
       g_free (mem_block);
     }
+  TRACE (GLIB_SLICE_FREE((void*)mem_block, mem_size));
 }
 
 void



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]