[gnome-photos/wip/rishi/misc-fixes: 5/14] Add EggCounter
- From: Debarshi Ray <debarshir src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-photos/wip/rishi/misc-fixes: 5/14] Add EggCounter
- Date: Mon, 28 Dec 2015 17:08:19 +0000 (UTC)
commit b92b6fb93452b576993b293ffe1c63773829039d
Author: Debarshi Ray <debarshir gnome org>
Date: Tue Dec 15 12:56:27 2015 +0100
Add EggCounter
configure.ac | 3 +
src/Makefile.am | 4 +
src/egg-counter.c | 621 +++++++++++++++++++++++++++++++++++++++++++++++++++++
src/egg-counter.h | 289 +++++++++++++++++++++++++
4 files changed, 917 insertions(+), 0 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index b2261a9..29b0a39 100644
--- a/configure.ac
+++ b/configure.ac
@@ -77,6 +77,9 @@ if test "$have_langinfo_measurement" = "yes"; then
[Define if _NL_MEASUREMENT_MEASUREMENT is available])
fi
+AC_CHECK_FUNCS([shm_open], [LIBRT=], [AC_CHECK_LIB([rt], [shm_open], [LIBRT=-lrt], [LIBRT=])])
+AC_SUBST(LIBRT)
+
PKG_CHECK_MODULES(BABL, [babl])
PKG_CHECK_MODULES(CAIRO, [cairo >= $CAIRO_MIN_VERSION cairo-gobject])
PKG_CHECK_MODULES(GFBGRAPH, [libgfbgraph-0.2 >= $GFBGRAPH_MIN_VERSION])
diff --git a/src/Makefile.am b/src/Makefile.am
index 7c2fb8f..c902e21 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -35,6 +35,8 @@ nodist_gnome_photos_SOURCES = \
$(NULL)
gnome_photos_SOURCES = \
+ egg-counter.c \
+ egg-counter.h \
gegl-gtk-enums.h \
gegl-gtk-view.c \
gegl-gtk-view.h \
@@ -308,6 +310,8 @@ gnome_photos_LDADD = \
$(JPEG_LIBS) \
$(TRACKER_LIBS) \
$(LIBM) \
+ $(LIBRT) \
+ -ldl \
$(top_builddir)/libgd/libgd.la \
$(NULL)
diff --git a/src/egg-counter.c b/src/egg-counter.c
new file mode 100644
index 0000000..6391e43
--- /dev/null
+++ b/src/egg-counter.c
@@ -0,0 +1,621 @@
+/* egg-counter.c
+ *
+ * Copyright (C) 2013-2015 Christian Hergert <christian hergert me>
+ *
+ * This file 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.1 of the License, or (at your option) any later version.
+ *
+ * This file 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 General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+#endif
+
+#ifdef __linux__
+# include <dlfcn.h>
+#endif
+#include <glib/gprintf.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "egg-counter.h"
+
+G_DEFINE_BOXED_TYPE (EggCounterArena, egg_counter_arena, egg_counter_arena_ref, egg_counter_arena_unref)
+
+#define NAME_FORMAT "/EggCounters-%u"
+#define MAGIC 0x71167125
+#define COUNTER_MAX_SHM (1024 * 1024 * 4)
+#define COUNTERS_PER_GROUP 8
+#define DATA_CELL_SIZE 64
+#define CELLS_PER_INFO (sizeof(CounterInfo) / DATA_CELL_SIZE)
+#define CELLS_PER_HEADER 2
+#define CELLS_PER_GROUP(ncpu) \
+ (((sizeof (CounterInfo) * COUNTERS_PER_GROUP) + \
+ (sizeof(EggCounterValue) * (ncpu))) / DATA_CELL_SIZE)
+#define EGG_MEMORY_BARRIER __sync_synchronize()
+
+typedef struct
+{
+ guint cell : 29; /* Counter groups starting cell */
+ guint position : 3; /* Index within counter group */
+ gchar category[20]; /* Counter category name. */
+ gchar name[32]; /* Counter name. */
+ gchar description[72]; /* Counter description */
+} CounterInfo __attribute__((aligned (DATA_CELL_SIZE)));
+
+G_STATIC_ASSERT (sizeof (CounterInfo) == 128);
+G_STATIC_ASSERT (CELLS_PER_INFO == 2);
+G_STATIC_ASSERT (CELLS_PER_GROUP(1) == 17);
+G_STATIC_ASSERT (CELLS_PER_GROUP(2) == 18);
+G_STATIC_ASSERT (CELLS_PER_GROUP(4) == 20);
+G_STATIC_ASSERT (CELLS_PER_GROUP(8) == 24);
+G_STATIC_ASSERT (CELLS_PER_GROUP(16) == 32);
+
+typedef struct
+{
+ gint64 values[8];
+} DataCell __attribute__((aligned (DATA_CELL_SIZE)));
+
+G_STATIC_ASSERT (sizeof (DataCell) == 64);
+
+typedef struct
+{
+ guint32 magic; /* Expected magic value */
+ guint32 size; /* Size of underlying shm file */
+ guint32 ncpu; /* Number of CPUs registered with */
+ guint32 first_offset; /* Offset to first counter info in cells */
+ guint32 n_counters; /* Number of CounterInfos */
+ gchar padding [108];
+} ShmHeader __attribute__((aligned (DATA_CELL_SIZE)));
+
+G_STATIC_ASSERT (sizeof(ShmHeader) == (DATA_CELL_SIZE * CELLS_PER_HEADER));
+
+struct _EggCounterArena
+{
+ gint ref_count;
+ guint arena_is_malloced : 1;
+ guint data_is_mmapped : 1;
+ guint is_local_arena : 1;
+ gsize n_cells;
+ DataCell *cells;
+ gsize data_length;
+ GPid pid;
+ guint n_counters;
+ GList *counters;
+};
+
+G_LOCK_DEFINE_STATIC (reglock);
+
+static void _egg_counter_init_getcpu (void) __attribute__ ((constructor));
+static guint (*_egg_counter_getcpu_helper) (void);
+
+gint64
+egg_counter_get (EggCounter *counter)
+{
+ gint64 value = 0;
+ guint ncpu;
+ guint i;
+
+ g_return_val_if_fail (counter, G_GINT64_CONSTANT (-1));
+
+ ncpu = g_get_num_processors ();
+
+ EGG_MEMORY_BARRIER;
+
+ for (i = 0; i < ncpu; i++)
+ value += counter->values [i].value;
+
+ return value;
+}
+
+void
+egg_counter_reset (EggCounter *counter)
+{
+ guint ncpu;
+ guint i;
+
+ g_return_if_fail (counter);
+
+ ncpu = g_get_num_processors ();
+
+ for (i = 0; i < ncpu; i++)
+ counter->values [i].value = 0;
+
+ EGG_MEMORY_BARRIER;
+}
+
+static void
+_egg_counter_arena_atexit (void)
+{
+ gchar name [32];
+ gint pid;
+
+ pid = getpid ();
+ g_snprintf (name, sizeof name, NAME_FORMAT, pid);
+ shm_unlink (name);
+}
+
+static void
+_egg_counter_arena_init_local (EggCounterArena *arena)
+{
+ ShmHeader *header;
+ gpointer mem;
+ unsigned pid;
+ gsize size;
+ gint page_size;
+ gint fd;
+ gchar name [32];
+
+ page_size = sysconf (_SC_PAGE_SIZE);
+
+ /* Implausible, but squashes warnings. */
+ if (page_size < 4096)
+ {
+ page_size = 4096;
+ size = page_size * 4;
+ goto use_malloc;
+ }
+
+ /*
+ * FIXME: https://bugzilla.gnome.org/show_bug.cgi?id=749280
+ *
+ * We have some very tricky work ahead of us to add unlimited numbers
+ * of counters at runtime. We basically need to avoid placing counters
+ * that could overlap a page.
+ */
+ size = page_size * 4;
+
+ arena->ref_count = 1;
+ arena->is_local_arena = TRUE;
+
+ if (getenv ("EGG_COUNTER_DISABLE_SHM"))
+ goto use_malloc;
+
+ pid = getpid ();
+ g_snprintf (name, sizeof name, NAME_FORMAT, pid);
+
+ if (-1 == (fd = shm_open (name, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR|S_IRGRP)))
+ goto use_malloc;
+
+ /*
+ * ftruncate() will cause reads to be zero. Therefore, we don't need to
+ * do write() of zeroes to initialize the shared memory area.
+ */
+ if (-1 == ftruncate (fd, size))
+ goto failure;
+
+ /*
+ * Memory map the shared memory segement so that we can store our counters
+ * within it. We need to layout the counters into the segment so that other
+ * processes can traverse and read the values by loading the shared page.
+ */
+ mem = mmap (NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+ if (mem == MAP_FAILED)
+ goto failure;
+
+ close (fd);
+ atexit (_egg_counter_arena_atexit);
+
+ arena->data_is_mmapped = TRUE;
+ arena->cells = mem;
+ arena->n_cells = (size / DATA_CELL_SIZE);
+ arena->data_length = size;
+
+ header = mem;
+ header->magic = MAGIC;
+ header->ncpu = g_get_num_processors ();
+ header->first_offset = CELLS_PER_HEADER;
+
+ EGG_MEMORY_BARRIER;
+
+ header->size = (guint32)arena->data_length;
+
+ return;
+
+failure:
+ shm_unlink (name);
+ close (fd);
+
+use_malloc:
+ g_warning ("Failed to allocate shared memory for counters. "
+ "Counters will not be available to external processes.");
+
+ arena->data_is_mmapped = FALSE;
+ arena->cells = g_malloc0 (size << 1);
+ arena->n_cells = (size / DATA_CELL_SIZE);
+ arena->data_length = size;
+
+ /*
+ * Make sure that we have a properly aligned allocation back from
+ * malloc. Since we are at least a page size, we should pretty much
+ * be guaranteed this, but better to check with posix_memalign().
+ */
+ if (posix_memalign ((void *)&arena->cells, page_size, size << 1) != 0)
+ {
+ perror ("posix_memalign()");
+ abort ();
+ }
+
+ header = (void *)arena->cells;
+ header->magic = MAGIC;
+ header->ncpu = g_get_num_processors ();
+ header->first_offset = CELLS_PER_HEADER;
+
+ EGG_MEMORY_BARRIER;
+
+ header->size = (guint32)arena->data_length;
+}
+
+static gboolean
+_egg_counter_arena_init_remote (EggCounterArena *arena,
+ GPid pid)
+{
+ ShmHeader header;
+ gssize count;
+ gchar name [32];
+ void *mem = NULL;
+ guint ncpu;
+ guint n_counters;
+ int i;
+ int fd = -1;
+
+ g_assert (arena != NULL);
+
+ ncpu = g_get_num_processors ();
+
+ arena->ref_count = 1;
+ arena->pid = pid;
+
+ g_snprintf (name, sizeof name, NAME_FORMAT, (int)pid);
+
+ fd = shm_open (name, O_RDONLY, 0);
+ if (fd < 0)
+ return FALSE;
+
+ count = pread (fd, &header, sizeof header, 0);
+
+ if ((count != sizeof header) ||
+ (header.magic != MAGIC) ||
+ (header.size > COUNTER_MAX_SHM) ||
+ (header.ncpu > g_get_num_processors ()))
+ goto failure;
+
+ n_counters = header.n_counters;
+
+ if (header.size <
+ CELLS_PER_HEADER + (((n_counters / COUNTERS_PER_GROUP) + 1) * CELLS_PER_GROUP(header.ncpu)))
+ goto failure;
+
+ mem = mmap (NULL, header.size, PROT_READ, MAP_SHARED, fd, 0);
+
+ if (mem == MAP_FAILED)
+ goto failure;
+
+ arena->is_local_arena = FALSE;
+ arena->data_is_mmapped = TRUE;
+ arena->cells = mem;
+ arena->n_cells = header.size / DATA_CELL_SIZE;
+ arena->data_length = header.size;
+ arena->counters = NULL;
+
+ /* Not strictly required, but helpful for now */
+ if (header.first_offset != CELLS_PER_HEADER)
+ goto failure;
+
+ for (i = 0; i < n_counters; i++)
+ {
+ CounterInfo *info;
+ EggCounter *counter;
+ guint group_start_cell;
+ guint group;
+ guint position;
+
+ group = i / COUNTERS_PER_GROUP;
+ position = i % COUNTERS_PER_GROUP;
+ group_start_cell = header.first_offset + (CELLS_PER_GROUP (ncpu) * group);
+
+ if (group_start_cell + CELLS_PER_GROUP (ncpu) >= arena->n_cells)
+ goto failure;
+
+ info = &(((CounterInfo *)&arena->cells[group_start_cell])[position]);
+
+ counter = g_new0 (EggCounter, 1);
+ counter->category = g_strndup (info->category, sizeof info->category);
+ counter->name = g_strndup (info->name, sizeof info->name);
+ counter->description = g_strndup (info->description, sizeof info->description);
+ counter->values = (EggCounterValue *)&arena->cells [info->cell].values[info->position];
+
+#if 0
+ g_print ("Counter discovered: cell=%u position=%u category=%s name=%s values=%p offset=%lu\n",
+ info->cell, info->position, info->category, info->name, counter->values,
+ (guint8*)counter->values - (guint8*)mem);
+#endif
+
+ arena->counters = g_list_prepend (arena->counters, counter);
+ }
+
+ close (fd);
+
+ return TRUE;
+
+failure:
+ close (fd);
+
+ if ((mem != NULL) && (mem != MAP_FAILED))
+ munmap (mem, header.size);
+
+ return FALSE;
+}
+
+EggCounterArena *
+egg_counter_arena_new_for_pid (GPid pid)
+{
+ EggCounterArena *arena;
+
+ arena = g_new0 (EggCounterArena, 1);
+
+ if (!_egg_counter_arena_init_remote (arena, pid))
+ {
+ g_free (arena);
+ return NULL;
+ }
+
+ return arena;
+}
+
+static void
+_egg_counter_arena_destroy (EggCounterArena *arena)
+{
+ g_assert (arena != NULL);
+
+ if (arena->data_is_mmapped)
+ munmap (arena->cells, arena->data_length);
+ else
+ g_free (arena->cells);
+
+ g_clear_pointer (&arena->counters, g_list_free);
+
+ arena->cells = NULL;
+
+ if (arena->arena_is_malloced)
+ g_free (arena);
+}
+
+EggCounterArena *
+egg_counter_arena_get_default (void)
+{
+ static EggCounterArena instance;
+ static gsize initialized;
+
+ if (G_UNLIKELY (g_once_init_enter (&initialized)))
+ {
+ _egg_counter_arena_init_local (&instance);
+ g_once_init_leave (&initialized, 1);
+ }
+
+ return &instance;
+}
+
+EggCounterArena *
+egg_counter_arena_ref (EggCounterArena *arena)
+{
+ g_return_val_if_fail (arena, NULL);
+ g_return_val_if_fail (arena->ref_count > 0, NULL);
+
+ g_atomic_int_inc (&arena->ref_count);
+
+ return arena;
+}
+
+void
+egg_counter_arena_unref (EggCounterArena *arena)
+{
+ g_return_if_fail (arena);
+ g_return_if_fail (arena->ref_count);
+
+ if (g_atomic_int_dec_and_test (&arena->ref_count))
+ _egg_counter_arena_destroy (arena);
+}
+
+void
+egg_counter_arena_foreach (EggCounterArena *arena,
+ EggCounterForeachFunc func,
+ gpointer user_data)
+{
+ GList *iter;
+
+ g_return_if_fail (arena != NULL);
+ g_return_if_fail (func != NULL);
+
+ for (iter = arena->counters; iter; iter = iter->next)
+ func (iter->data, user_data);
+}
+
+void
+egg_counter_arena_register (EggCounterArena *arena,
+ EggCounter *counter)
+{
+ CounterInfo *info;
+ guint group;
+ guint ncpu;
+ guint position;
+ guint group_start_cell;
+
+ g_return_if_fail (arena != NULL);
+ g_return_if_fail (counter != NULL);
+
+ if (!arena->is_local_arena)
+ {
+ g_warning ("Cannot add counters to a remote arena.");
+ return;
+ }
+
+ ncpu = g_get_num_processors ();
+
+ G_LOCK (reglock);
+
+ /*
+ * Get the counter group and position within the group of the counter.
+ */
+ group = arena->n_counters / COUNTERS_PER_GROUP;
+ position = arena->n_counters % COUNTERS_PER_GROUP;
+
+ /*
+ * Get the starting cell for this group. Cells roughly map to cachelines.
+ */
+ group_start_cell = CELLS_PER_HEADER + (CELLS_PER_GROUP (ncpu) * group);
+ info = &((CounterInfo *)&arena->cells [group_start_cell])[position];
+
+ g_assert (position < COUNTERS_PER_GROUP);
+ g_assert (group_start_cell < arena->n_cells);
+
+ /*
+ * Store information about the counter in the SHM area. Also, update
+ * the counter values pointer to map to the right cell in the SHM zone.
+ */
+ info->cell = group_start_cell + (COUNTERS_PER_GROUP * CELLS_PER_INFO);
+ info->position = position;
+ g_snprintf (info->category, sizeof info->category, "%s", counter->category);
+ g_snprintf (info->description, sizeof info->description, "%s", counter->description);
+ g_snprintf (info->name, sizeof info->name, "%s", counter->name);
+ counter->values = (EggCounterValue *)&arena->cells [info->cell].values[info->position];
+
+#if 0
+ g_print ("Counter registered: cell=%u position=%u category=%s name=%s\n",
+ info->cell, info->position, info->category, info->name);
+#endif
+
+ /*
+ * Track the counter address, so we can _foreach() them.
+ */
+ arena->counters = g_list_append (arena->counters, counter);
+ arena->n_counters++;
+
+ /*
+ * Now notify remote processes of the counter.
+ */
+ EGG_MEMORY_BARRIER;
+ ((ShmHeader *)&arena->cells[0])->n_counters++;
+
+ G_UNLOCK (reglock);
+}
+
+#ifdef __linux__
+static void *
+_egg_counter_find_getcpu_in_vdso (void)
+{
+ static const gchar *vdso_names[] = {
+ "linux-vdso.so.1",
+ "linux-vdso32.so.1",
+ "linux-vdso64.so.1",
+ NULL
+ };
+ static const gchar *sym_names[] = {
+ "__kernel_getcpu",
+ "__vdso_getcpu",
+ NULL
+ };
+ gint i;
+
+ for (i = 0; vdso_names [i]; i++)
+ {
+ void *lib;
+ gint j;
+
+ lib = dlopen (vdso_names [i], RTLD_NOW | RTLD_GLOBAL);
+ if (lib == NULL)
+ continue;
+
+ for (j = 0; sym_names [j]; j++)
+ {
+ void *sym;
+
+ sym = dlsym (lib, sym_names [j]);
+ if (!sym)
+ continue;
+
+ return sym;
+ }
+
+ dlclose (lib);
+ }
+
+ return NULL;
+}
+
+static guint (*_egg_counter_getcpu_vdso_raw) (int *cpu,
+ int *node,
+ void *tcache);
+
+static guint
+_egg_counter_getcpu_vdso_helper (void)
+{
+ int cpu;
+ _egg_counter_getcpu_vdso_raw (&cpu, NULL, NULL);
+ return cpu;
+}
+#endif
+
+static guint
+_egg_counter_getcpu_fallback (void)
+{
+ return 0;
+}
+
+#ifdef EGG_HAVE_RDTSCP
+static guint
+_egg_counter_getcpu_rdtscp (void)
+{
+ return egg_get_current_cpu_rdtscp ();
+}
+#endif
+
+static void
+_egg_counter_init_getcpu (void)
+{
+
+#ifdef EGG_HAVE_RDTSCP
+ _egg_counter_getcpu_helper = _egg_counter_getcpu_rdtscp;
+#endif
+
+#ifdef __linux__
+ _egg_counter_getcpu_vdso_raw = _egg_counter_find_getcpu_in_vdso ();
+
+ if (_egg_counter_getcpu_vdso_raw)
+ {
+ _egg_counter_getcpu_helper = _egg_counter_getcpu_vdso_helper;
+ return;
+ }
+#endif
+
+#ifdef HAVE_SCHED_GETCPU
+ _egg_counter_getcpu_helper = (guint (*) (void))sched_getcpu;
+#endif
+
+ _egg_counter_getcpu_helper = _egg_counter_getcpu_fallback;
+}
+
+guint
+egg_get_current_cpu_call (void)
+{
+ return _egg_counter_getcpu_helper ();
+}
diff --git a/src/egg-counter.h b/src/egg-counter.h
new file mode 100644
index 0000000..412d3b9
--- /dev/null
+++ b/src/egg-counter.h
@@ -0,0 +1,289 @@
+/* egg-counter.h
+ *
+ * Copyright (C) 2013-2015 Christian Hergert <christian hergert me>
+ *
+ * This file 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.1 of the License, or (at your option) any later version.
+ *
+ * This file 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 General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Additionally, this file does not claim copyright over the expansion
+ * of macros in your source program.
+ */
+
+#ifndef EGG_COUNTER_H
+#define EGG_COUNTER_H
+
+#include <glib-object.h>
+
+/*
+ * History
+ * =======
+ *
+ * EggCounter is a performance counter based on ideas from previous work
+ * on high performance counters. They are not guaranteed to be 100%
+ * correct, but they approach that with no synchronization given new
+ * enough hardware. In particular, we use %ecx from rdtscp (the core id)
+ * to determine which cachline to increment the counter within.
+ *
+ * Given a counter, the value will be split up int NCPU cachelines where
+ * NCPU is the number of cores returned from get_nprocs() (on Linux).
+ *
+ * Updating the counter is very cheap, reading back the counter requires
+ * a volatile read of each cacheline. Again, no correctness is guaranteed.
+ *
+ * In practice, very few values are lost even during tight competing loops.
+ * A loss can happen when the thread is pre-empted between the %rdtscp
+ * instruction and the addq increment (on x86_64).
+ *
+ *
+ * Using EggCounter
+ * ================
+ *
+ * To define a counter, you must have support for constructor attributes.
+ *
+ * EGG_DEFINE_COUNTER (Symbol, "Category", "Name", "Description")
+ *
+ * To increment the counter in a function of your choice (but within the
+ * same module), use EGG_COUNTER_ADD, EGG_COUNTER_INC, EGG_COUNTER_DEC.
+ *
+ * EGG_COUNTER_ADD (Symbol);
+ *
+ *
+ * Architecture Support
+ * ====================
+ *
+ * If you are not on x86_64, or are missing the rdtscp instruction, a 64-bit
+ * atomic will be performed using __sync_fetch_and_add8(). Clearly, we can
+ * do some more work here to abstract which implementation is used, but we
+ * only support GCC and Clang today, which both have that intrinsic. Some
+ * architectures may not have it (such as 32-bit PPC), but I'm not too
+ * concerned about that at the moment.
+ *
+ * The counters are mapped into a shared memory zone using shm_open() and
+ * mmap(). An external program can then discover the available counters
+ * and print them without blocking the target program. It simply must
+ * perform the reads in a volatile manner just like the target process
+ * would need to do for readback.
+ *
+ * EggCounterArena provides a helper to walk through the counters in the
+ * shared memory zone. egg_counter_arena_foreach().
+ *
+ * You cannot remove a counter once it has been registered.
+ *
+ *
+ * Accessing Counters Remotely
+ * ===========================
+ *
+ * You can access the counters from out of process. By opening the SHM zone
+ * and reading the contents from each cachline, you can get the approximate
+ * state of the target application without blocking it.
+ *
+ * EggCounterArena provides a helper for you to do this.
+ *
+ * EggCounterArena *arena;
+ *
+ * arena = egg_counter_arena_new_for_pid (other_process_pid);
+ * egg_counter_arena_foreach (arena, my_counter_callback, user_data);
+ *
+ *
+ * Data Layout
+ * ===========
+ *
+ * The layout of the shared memory zone is broken into "cells". Each cell
+ * is an approximate cacheline (64-bytes) on modern Intel hardware. Indexes
+ * to data locations are represented in cells to simplify the math and
+ * allow the compiler to know we are working with properly aligned structures.
+ *
+ * The base pointer in EggCounter.values is not 64-byte aligned! It is 8-byte
+ * aligned and points to the offset within the cacheline for that counter.
+ * We pack 8 64-bit counters into a single cacheline. This allows us to avoid
+ * an extra MOV instruction when incrementing since we only need to perform
+ * the offset from the base pointer.
+ *
+ * The first two cells are the header which contain information about the
+ * underlying shm file and how large the mmap() range should be.
+ *
+ * After that, begin the counters.
+ *
+ * The counters are layed out in groups of 8 counters.
+ *
+ * [8 CounterInfo Structs (128-bytes each)][N_CPU Data Zones (64-byte each)]
+ *
+ * See egg-counter.c for more information on the contents of these structures.
+ *
+ *
+ * Build System Requirements
+ * =========================
+ *
+ * We need to know if rdtscp is available at compile time. In an effort
+ * to keep the headers as portable as possible (if that matters here?) we
+ * require that you define EGG_HAVE_RDTSCP if the instruction is supported.
+ *
+ * An example for autoconf might be similar to the following:
+ *
+ * AC_MSG_CHECKING([for fast counters with rdtscp])
+ * AC_RUN_IFELSE(
+ * [AC_LANG_SOURCE([[
+ * #include <x86intrin.h>
+ * int main (int argc, char *argv[]) { int cpu; __builtin_ia32_rdtscp (&cpu); return 0; }]])],
+ * [have_rdtscp=yes],
+ * [have_rdtscp=no])
+ * AC_MSG_RESULT([$have_rdtscp])
+ * AS_IF([test "$have_rdtscp" = "yes"],
+ * [CFLAGS="$CFLAGS -DEGG_HAVE_RDTSCP"])
+ */
+
+G_BEGIN_DECLS
+
+#ifdef EGG_HAVE_RDTSCP
+# include <x86intrin.h>
+ static inline guint
+ egg_get_current_cpu_rdtscp (void)
+ {
+ /*
+ * This extracts the IA32_TSC_AUX into the ecx register. On Linux,
+ * that value contains a value with the bottom 12 bits being the
+ * cpu identifier, and the next 10 bits being the node group.
+ */
+ guint aux;
+ __builtin_ia32_rdtscp (&aux);
+ return aux & 0xFFF;
+ }
+# define egg_get_current_cpu() egg_get_current_cpu_rdtscp()
+#elif defined(__linux__)
+# define egg_get_current_cpu() egg_get_current_cpu_call()
+#else
+# define egg_get_current_cpu() 0
+# define EGG_COUNTER_REQUIRES_ATOMIC 1
+#endif
+
+/**
+ * EGG_DEFINE_COUNTER:
+ * @Identifier: The symbol name of the counter
+ * @Category: A string category for the counter.
+ * @Name: A string name for the counter.
+ * @Description: A string description for the counter.
+ *
+ * |[<!-- language="C" -->
+ * EGG_DEFINE_COUNTER (my_counter, "My", "Counter", "My Counter Description");
+ * ]|
+ */
+#define EGG_DEFINE_COUNTER(Identifier, Category, Name, Description) \
+ static EggCounter Identifier##_ctr = { NULL, Category, Name, Description }; \
+ static void Identifier##_ctr_init (void) __attribute__((constructor)); \
+ static void \
+ Identifier##_ctr_init (void) \
+ { \
+ egg_counter_arena_register (egg_counter_arena_get_default(), &Identifier##_ctr); \
+ }
+
+/**
+ * EGG_COUNTER_INC:
+ * @Identifier: The identifier of the counter.
+ *
+ * Increments the counter @Identifier by 1.
+ */
+#define EGG_COUNTER_INC(Identifier) EGG_COUNTER_ADD(Identifier, G_GINT64_CONSTANT(1))
+
+/**
+ * EGG_COUNTER_DEC:
+ * @Identifier: The identifier of the counter.
+ *
+ * Decrements the counter @Identifier by 1.
+ */
+#define EGG_COUNTER_DEC(Identifier) EGG_COUNTER_SUB(Identifier, G_GINT64_CONSTANT(1))
+
+/**
+ * EGG_COUNTER_SUB:
+ * @Identifier: The identifier of the counter.
+ * @Count: the amount to subtract.
+ *
+ * Subtracts from the counter identified by @Identifier by @Count.
+ */
+#define EGG_COUNTER_SUB(Identifier, Count) EGG_COUNTER_ADD(Identifier, (-(Count)))
+
+/**
+ * EGG_COUNTER_ADD:
+ * @Identifier: The identifier of the counter.
+ * @Count: the amount to add to the counter.
+ *
+ * Adds @Count to @Identifier.
+ *
+ * This operation is not guaranteed to have full correctness. It tries to find
+ * a happy medium between fast, and accurate. When possible, the %rdtscp
+ * instruction is used to get a cacheline owned by the executing CPU, to avoid
+ * collisions. However, this is not guaranteed as the thread could be swapped
+ * between the calls to %rdtscp and %addq (on 64-bit Intel).
+ *
+ * Other platforms have fallbacks which may give different guarantees, such as
+ * using atomic operations (and therefore, memory barriers).
+ *
+ * See #EggCounter for more information.
+ */
+#ifdef EGG_COUNTER_REQUIRES_ATOMIC
+# define EGG_COUNTER_ADD(Identifier, Count) \
+ G_STMT_START { \
+ __sync_add_and_fetch ((gint64 *)&Identifier##_ctr.values[0], ((gint64)(Count))); \
+ } G_STMT_END
+#else
+# define EGG_COUNTER_ADD(Identifier, Count) \
+ G_STMT_START { \
+ Identifier##_ctr.values[egg_get_current_cpu()].value += ((gint64)(Count)); \
+ } G_STMT_END
+#endif
+
+typedef struct _EggCounter EggCounter;
+typedef struct _EggCounterArena EggCounterArena;
+typedef struct _EggCounterValue EggCounterValue;
+
+/**
+ * EggCounterForeachFunc:
+ * @counter: the counter.
+ * @user_data: data supplied to egg_counter_arena_foreach().
+ *
+ * Function prototype for callbacks provided to egg_counter_arena_foreach().
+ */
+typedef void (*EggCounterForeachFunc) (EggCounter *counter,
+ gpointer user_data);
+
+struct _EggCounter
+{
+ /*< Private >*/
+ EggCounterValue *values;
+ const gchar *category;
+ const gchar *name;
+ const gchar *description;
+} __attribute__ ((aligned(8)));
+
+struct _EggCounterValue
+{
+ volatile gint64 value;
+ gint64 padding [7];
+} __attribute__ ((aligned(8)));
+
+GType egg_counter_arena_get_type (void);
+guint egg_get_current_cpu_call (void);
+EggCounterArena *egg_counter_arena_get_default (void);
+EggCounterArena *egg_counter_arena_new_for_pid (GPid pid);
+EggCounterArena *egg_counter_arena_ref (EggCounterArena *arena);
+void egg_counter_arena_unref (EggCounterArena *arena);
+void egg_counter_arena_register (EggCounterArena *arena,
+ EggCounter *counter);
+void egg_counter_arena_foreach (EggCounterArena *arena,
+ EggCounterForeachFunc func,
+ gpointer user_data);
+void egg_counter_reset (EggCounter *counter);
+gint64 egg_counter_get (EggCounter *counter);
+
+G_END_DECLS
+
+#endif /* EGG_COUNTER_H */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]