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.
This commit is contained in:
Christian Hergert
2020-02-13 18:58:03 -08:00
parent c546d31ad9
commit 04d599c718
2 changed files with 123 additions and 66 deletions

View File

@ -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__ */

View File

@ -16,6 +16,8 @@
#include <sysprof-capture.h>
#include <unistd.h>
#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 *