From bef9efd0a99a9a3bd6a2d713423edc37d6a38f21 Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Fri, 18 Dec 2009 21:25:47 +0100 Subject: [PATCH] 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(-) create mode 100644 glib/glib.stp.in create mode 100644 glib/glib_probes.d create mode 100644 glib/glib_trace.h diff --git a/configure.in b/configure.in index 6cc3bc94..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 configure option. + + + The optional support for DTrace requires the sys/sdt.h header, + which is provided by SystemTap on Linux. To build GLib without DTrace, use the + configure option. + + + + + The optional support for SystemTap can be disabled with the + configure option. + + @@ -239,6 +252,14 @@ How to compile GLib itself --enable-selinux + --disable-dtrace + --enable-dtrace + + + --disable-systemtap + --enable-systemtap + + --with-runtime-libdir=RELPATH @@ -562,6 +583,27 @@ How to compile GLib itself be included. + + + <systemitem>--disable-dtrace</systemitem> and + <systemitem>--enable-dtrace</systemitem> + + + By default the configure script will + detect if DTrace support is available, and use it. + + + + + <systemitem>--disable-systemtap</systemitem> and + <systemitem>--enable-systemtap</systemitem> + + + This option requires DTrace support. If it is available, then + the configure script will also check for + the presence of SystemTap. + + <systemitem>--with-runtime-libdir=RELPATH</systemitem> 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. +SystemTap + + +SystemTap is a dynamic whole-system +analysis toolkit. GLib ships with a file glib.stp which defines a +set of probe points, which you can hook into with custom SystemTap scripts. +See the files glib.stp and gobject.stp which +are in your shared SystemTap scripts directory. + + + + + Memory statistics 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 + */ + +#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 /* 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 -- 2.7.4