From 04d599c718e30c2ef77f9d00489681eabc1a57fb Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 13 Feb 2020 18:58:03 -0800 Subject: [PATCH] preload: port memory collector to collector API This uses a simplified form of collection without writing to capture files directly. The data is written into a ring buffer for Sysprof to pick up and copy into the real destination file. Using the mmap() ring buffer allows loss of data when Sysprof cannot keep up, but on the other hand allows the inferior to be fast enough to be useful. --- src/libsysprof/preload/gconstructor.h | 94 ++++++++++++++++++ .../preload/sysprof-memory-collector.c | 95 ++++++------------- 2 files changed, 123 insertions(+), 66 deletions(-) create mode 100644 src/libsysprof/preload/gconstructor.h diff --git a/src/libsysprof/preload/gconstructor.h b/src/libsysprof/preload/gconstructor.h new file mode 100644 index 00000000..df98f835 --- /dev/null +++ b/src/libsysprof/preload/gconstructor.h @@ -0,0 +1,94 @@ +/* + If G_HAS_CONSTRUCTORS is true then the compiler support *both* constructors and + destructors, in a sane way, including e.g. on library unload. If not you're on + your own. + + Some compilers need #pragma to handle this, which does not work with macros, + so the way you need to use this is (for constructors): + + #ifdef G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA + #pragma G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(my_constructor) + #endif + G_DEFINE_CONSTRUCTOR(my_constructor) + static void my_constructor(void) { + ... + } + +*/ + +#ifndef __GTK_DOC_IGNORE__ + +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7) + +#define G_HAS_CONSTRUCTORS 1 + +#define G_DEFINE_CONSTRUCTOR(_func) static void __attribute__((constructor)) _func (void); +#define G_DEFINE_DESTRUCTOR(_func) static void __attribute__((destructor)) _func (void); + +#elif defined (_MSC_VER) && (_MSC_VER >= 1500) +/* Visual studio 2008 and later has _Pragma */ + +#define G_HAS_CONSTRUCTORS 1 + +#define G_DEFINE_CONSTRUCTOR(_func) \ + static void _func(void); \ + static int _func ## _wrapper(void) { _func(); return 0; } \ + __pragma(section(".CRT$XCU",read)) \ + __declspec(allocate(".CRT$XCU")) static int (* _array ## _func)(void) = _func ## _wrapper; + +#define G_DEFINE_DESTRUCTOR(_func) \ + static void _func(void); \ + static int _func ## _constructor(void) { atexit (_func); return 0; } \ + __pragma(section(".CRT$XCU",read)) \ + __declspec(allocate(".CRT$XCU")) static int (* _array ## _func)(void) = _func ## _constructor; + +#elif defined (_MSC_VER) + +#define G_HAS_CONSTRUCTORS 1 + +/* Pre Visual studio 2008 must use #pragma section */ +#define G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA 1 +#define G_DEFINE_DESTRUCTOR_NEEDS_PRAGMA 1 + +#define G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(_func) \ + section(".CRT$XCU",read) +#define G_DEFINE_CONSTRUCTOR(_func) \ + static void _func(void); \ + static int _func ## _wrapper(void) { _func(); return 0; } \ + __declspec(allocate(".CRT$XCU")) static int (*p)(void) = _func ## _wrapper; + +#define G_DEFINE_DESTRUCTOR_PRAGMA_ARGS(_func) \ + section(".CRT$XCU",read) +#define G_DEFINE_DESTRUCTOR(_func) \ + static void _func(void); \ + static int _func ## _constructor(void) { atexit (_func); return 0; } \ + __declspec(allocate(".CRT$XCU")) static int (* _array ## _func)(void) = _func ## _constructor; + +#elif defined(__SUNPRO_C) + +/* This is not tested, but i believe it should work, based on: + * http://opensource.apple.com/source/OpenSSL098/OpenSSL098-35/src/fips/fips_premain.c + */ + +#define G_HAS_CONSTRUCTORS 1 + +#define G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA 1 +#define G_DEFINE_DESTRUCTOR_NEEDS_PRAGMA 1 + +#define G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(_func) \ + init(_func) +#define G_DEFINE_CONSTRUCTOR(_func) \ + static void _func(void); + +#define G_DEFINE_DESTRUCTOR_PRAGMA_ARGS(_func) \ + fini(_func) +#define G_DEFINE_DESTRUCTOR(_func) \ + static void _func(void); + +#else + +/* constructors not supported for this compiler */ + +#endif + +#endif /* __GTK_DOC_IGNORE__ */ diff --git a/src/libsysprof/preload/sysprof-memory-collector.c b/src/libsysprof/preload/sysprof-memory-collector.c index 1e6b240c..a9b81acd 100644 --- a/src/libsysprof/preload/sysprof-memory-collector.c +++ b/src/libsysprof/preload/sysprof-memory-collector.c @@ -16,6 +16,8 @@ #include #include +#include "gconstructor.h" + typedef void *(* RealMalloc) (size_t); typedef void (* RealFree) (void *); typedef void *(* RealCalloc) (size_t, size_t); @@ -36,10 +38,8 @@ static void *scratch_realloc (void *, size_t); static void *scratch_calloc (size_t, size_t); static void scratch_free (void *); -static G_LOCK_DEFINE (writer); -static SysprofCaptureWriter *writer; +static int collector_ready; static int hooked; -static int pid; static ScratchAlloc scratch; static RealCalloc real_calloc = scratch_calloc; static RealFree real_free = scratch_free; @@ -49,6 +49,22 @@ static RealAlignedAlloc real_aligned_alloc; static RealPosixMemalign real_posix_memalign; static RealMemalign real_memalign; +#if defined (G_HAS_CONSTRUCTORS) +# ifdef G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA +# pragma G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(collector_init_ctor) +# endif +G_DEFINE_CONSTRUCTOR(collector_init_ctor) +#else +# error Your platform/compiler is missing constructor support +#endif + +static void +collector_init_ctor (void) +{ + sysprof_collector_init (); + collector_ready = TRUE; +} + static guint backtrace_func (SysprofCaptureAddress *addrs, guint n_addrs, @@ -134,19 +150,9 @@ scratch_free (void *ptr) return; } -static void -flush_writer (void) -{ - G_LOCK (writer); - sysprof_capture_writer_flush (writer); - G_UNLOCK (writer); -} - static void hook_memtable (void) { - const gchar *env; - if (hooked) return; @@ -161,70 +167,27 @@ hook_memtable (void) real_memalign = dlsym (RTLD_NEXT, "memalign"); unsetenv ("LD_PRELOAD"); - - pid = getpid (); - - /* TODO: We want an API that let's us create a new writer - * per-thread instead of something like this (or using an - * environment variable). That will require a control channel - * to sysprof to request new writer/muxed APIs. - */ - - env = getenv ("MEMPROF_TRACE_FD"); - - if (env != NULL) - { - int fd = atoi (env); - - if (fd > 0) - writer = sysprof_capture_writer_new_from_fd (fd, 0); - } - - if (writer == NULL) - writer = sysprof_capture_writer_new ("memory.syscap", 0); - - atexit (flush_writer); } -#define gettid() syscall(__NR_gettid, 0) - static inline void track_malloc (void *ptr, size_t size) { - if G_UNLIKELY (!writer) - return; - - G_LOCK (writer); - sysprof_capture_writer_add_allocation (writer, - SYSPROF_CAPTURE_CURRENT_TIME, - sched_getcpu (), - pid, - gettid(), - GPOINTER_TO_SIZE (ptr), - size, - backtrace_func, - NULL); - G_UNLOCK (writer); + if G_LIKELY (ptr && collector_ready) + sysprof_collector_allocate (GPOINTER_TO_SIZE (ptr), + size, + backtrace_func, + NULL); } static inline void track_free (void *ptr) { - if G_UNLIKELY (!writer) - return; - - G_LOCK (writer); - sysprof_capture_writer_add_allocation (writer, - SYSPROF_CAPTURE_CURRENT_TIME, - sched_getcpu (), - pid, - gettid(), - GPOINTER_TO_SIZE (ptr), - 0, - backtrace_func, - 0); - G_UNLOCK (writer); + if G_LIKELY (ptr && collector_ready) + sysprof_collector_allocate (GPOINTER_TO_SIZE (ptr), + 0, + backtrace_func, + NULL); } void *