[gjs/wip/tko/116-mem-track: 1/4] context: dump heaps on signal



commit 4eb25d23deaac3f324931c9dffff308c0f060bef
Author: Tommi Komulainen <tko litl com>
Date:   Mon Jan 26 14:05:23 2009 +0000

    context: dump heaps on signal
    
    When GJS_DEBUG_HEAP_OUTPUT environment variable is set, set up SIGUSR1
    signal handler to dump JS heaps (using JS_DumpHeap) to file to aid
    debugging memory leaks.
    
    The log files generated will be ${GJS_DEBUG_HEAP_OUTPUT}.${pid}.${counter}
    
    Usage:
    $ GJS_DEBUG_HEAP_OUTPUT=/tmp/gjs-heap gjs-console ...
    $ kill -USR1 `pidof gjs-console`
    $ less /tmp/gjs-heap.`pidof gjs-console`.0
    
    (Philip Chimento: rebased and updated coding style)

 gjs/context.cpp | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 79 insertions(+)
---
diff --git a/gjs/context.cpp b/gjs/context.cpp
index aa9b2a09..d281dd91 100644
--- a/gjs/context.cpp
+++ b/gjs/context.cpp
@@ -24,6 +24,9 @@
 #include <config.h>
 
 #include <array>
+#include <signal.h>
+#include <sys/types.h>
+#include <unistd.h>
 #include <unordered_map>
 
 #include <gio/gio.h>
@@ -34,6 +37,7 @@
 #include "importer.h"
 #include "jsapi-util.h"
 #include "jsapi-wrapper.h"
+#include "mem.h"
 #include "native.h"
 #include "profiler-private.h"
 #include "byteArray.h"
@@ -129,6 +133,79 @@ enum {
 static GMutex contexts_lock;
 static GList *all_contexts = NULL;
 
+static char *dump_heap_output = nullptr;
+static unsigned dump_heap_idle_id = 0;
+
+static void
+gjs_context_dump_heap(GjsContext *js_context,
+                      FILE *fp)
+{
+    js::DumpHeap(js_context->context, fp, js::IgnoreNurseryObjects);
+}
+
+static void
+gjs_context_dump_heaps(void)
+{
+    static unsigned counter = 0;
+
+    gjs_memory_report("signal handler", false);
+
+    /* dump to sequential files to allow easier comparisons */
+    GjsAutoChar filename = g_strdup_printf("%s.%jd.%u", dump_heap_output,
+                                           intmax_t(getpid()), counter);
+    ++counter;
+
+    FILE *fp = fopen(filename, "w");
+    if (!fp)
+        return;
+
+    for (GList *l = all_contexts; l; l = g_list_next(l)) {
+        auto js_context = static_cast<GjsContext *>(l->data);
+        gjs_context_dump_heap(js_context, fp);
+    }
+
+    fclose(fp);
+}
+
+static gboolean
+dump_heap_idle(gpointer user_data)
+{
+    dump_heap_idle_id = 0;
+
+    gjs_context_dump_heaps();
+
+    return false;
+}
+
+static void
+dump_heap_signal_handler(int signum)
+{
+    if (dump_heap_idle_id == 0)
+        dump_heap_idle_id = g_idle_add_full(G_PRIORITY_HIGH_IDLE,
+                                            dump_heap_idle, nullptr, nullptr);
+}
+
+static void
+setup_dump_heap(void)
+{
+    static bool dump_heap_initialized = false;
+    if (!dump_heap_initialized) {
+        dump_heap_initialized = true;
+
+        /* install signal handler only if environment variable is set */
+        const char *heap_output = g_getenv("GJS_DEBUG_HEAP_OUTPUT");
+        if (heap_output) {
+            struct sigaction sa;
+
+            dump_heap_output = g_strdup(heap_output);
+
+            memset(&sa, 0, sizeof(sa));
+            sa.sa_handler = dump_heap_signal_handler;
+            sigaction(SIGUSR1, &sa, nullptr);
+        }
+    }
+}
+
 static void
 gjs_context_init(GjsContext *js_context)
 {
@@ -410,6 +487,8 @@ gjs_context_constructed(GObject *object)
     g_mutex_lock (&contexts_lock);
     all_contexts = g_list_prepend(all_contexts, object);
     g_mutex_unlock (&contexts_lock);
+
+    setup_dump_heap();
 }
 
 static void


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