about summary refs log tree commit homepage
path: root/mwrap_core.h
diff options
context:
space:
mode:
Diffstat (limited to 'mwrap_core.h')
-rw-r--r--mwrap_core.h166
1 files changed, 123 insertions, 43 deletions
diff --git a/mwrap_core.h b/mwrap_core.h
index 02b60f3..15df857 100644
--- a/mwrap_core.h
+++ b/mwrap_core.h
@@ -9,8 +9,8 @@
 #        define MWRAP_PERL 0
 #endif
 
-#if !MWRAP_PERL
-typedef void COP;
+#ifndef MWRAP_RUBY
+#        define MWRAP_RUBY 0
 #endif
 
 /* set a sensible max to avoid stack overflows */
@@ -18,20 +18,13 @@ typedef void COP;
 #        define        MWRAP_BT_MAX 32
 #endif
 
-
-#if MWRAP_PERL
-#        include "EXTERN.h"
-#        include "perl.h"
-#        include "XSUB.h"
-#        include "embed.h"
-#        include "ppport.h"
+#ifndef _GNU_SOURCE
+#        define _GNU_SOURCE
 #endif
-
 #include <execinfo.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <dlfcn.h>
 #include <assert.h>
 #include <errno.h>
 #include <sys/types.h>
@@ -44,6 +37,14 @@ typedef void COP;
 #include <urcu/rculist.h>
 #include <limits.h>
 
+#if MWRAP_PERL
+#        include "EXTERN.h"
+#        include "perl.h"
+#        include "XSUB.h"
+#        include "embed.h"
+#        include "ppport.h"
+#endif
+
 /*
  * XXH3 (truncated to 32-bits) seems to provide a ~2% speedup.
  * XXH32 doesn't show improvements over jhash despite rculfhash
@@ -75,12 +76,24 @@ static uint32_t bt_req_depth;
 #if MWRAP_PERL
 extern pthread_key_t __attribute__((weak)) PL_thr_key;
 extern const char __attribute__((weak)) PL_memory_wrap[]; /* needed for -O0 */
-#endif
+#        if !defined(PERL_IMPLICIT_CONTEXT)
+static size_t *root_locating; /* determines if PL_curcop is our thread */
+#        endif
+#endif /* MWRAP_PERL */
 
+#if MWRAP_RUBY
+static void mw_ruby_set_generation(size_t *, size_t);
+#        define SET_GENERATION(gen, size) mw_ruby_set_generation(gen, size)
+static size_t last_gc_count; /* for httpd which runs in a non-GVL thread */
+#endif /* MWRAP_RUBY */
+
+#ifndef SET_GENERATION /* C-only builds w/o Perl|Ruby */
+#        define SET_GENERATION(gen, size) \
+                *gen = uatomic_add_return(&total_bytes_inc, size)
+#endif /* !SET_GENERATION */
+
+/* generic stuff: */
 static MWRAP_TSD size_t locating;
-#if MWRAP_PERL && !defined(PERL_IMPLICIT_CONTEXT)
-static size_t *root_locating; /* determines if PL_curcop is our thread */
-#endif
 static struct cds_lfht *files, *totals;
 union padded_mutex {
         pthread_mutex_t mtx;
@@ -142,6 +155,7 @@ static void *my_mempcpy(void *dest, const void *src, size_t n)
 
 /*
  * only for interpreted sources (Perl/Ruby/etc), not backtrace_symbols* files
+ * Allocated via real_malloc / real_free
  */
 struct src_file {
         struct cds_lfht_node nd; /* <=> files table */
@@ -179,7 +193,7 @@ struct alloc_hdr {
         struct cds_list_head anode; /* <=> src_loc.allocs */
         union {
                 struct {
-                        size_t gen; /* global age */
+                        size_t gen; /* global age || rb_gc_count() */
                         struct src_loc *loc;
                 } live;
                 struct rcu_head dead;
@@ -319,20 +333,6 @@ again:
         return l;
 }
 
-static const COP *mwp_curcop(void)
-{
-#if MWRAP_PERL
-        if (&PL_thr_key) { /* are we even in a Perl process? */
-#        ifdef PERL_IMPLICIT_CONTEXT
-                if (aTHX) return PL_curcop;
-#        else /* !PERL_IMPLICIT_CONTEXT */
-                if (&locating == root_locating) return PL_curcop;
-#        endif /* PERL_IMPLICIT_CONTEXT */
-        }
-#endif /* MWRAP_PERL */
-        return NULL;
-}
-
 static uint32_t do_hash(const void *p, size_t len)
 {
 #if defined(XXH3_64bits)
@@ -372,18 +372,43 @@ static struct src_file *src_file_get(struct cds_lfht *t, struct src_file *k,
         return cur ? caa_container_of(cur, struct src_file, nd) : NULL;
 }
 
-#if !MWRAP_PERL
-#        define CopFILE(cop) NULL
-#        define CopLINE(cop) 0
-#endif
-static struct src_loc *assign_line(size_t size, const COP *cop,
-                                struct src_loc *sl)
+#if MWRAP_PERL
+static const COP *mwp_curcop(void)
 {
-        /* avoid vsnprintf or anything which could call malloc here: */
+        if (&PL_thr_key) { /* are we even in a Perl process? */
+#        ifdef PERL_IMPLICIT_CONTEXT
+                if (aTHX) return PL_curcop;
+#        else /* !PERL_IMPLICIT_CONTEXT */
+                if (&locating == root_locating) return PL_curcop;
+#        endif /* PERL_IMPLICIT_CONTEXT */
+        }
+        return NULL;
+}
+
+static const char *mw_perl_src_file_cstr(unsigned *lineno)
+{
+        const COP *cop = mwp_curcop();
         if (!cop) return NULL;
         const char *fn = CopFILE(cop);
         if (!fn) return NULL;
-        unsigned lineno = CopLINE(cop);
+        *lineno = CopLINE(cop);
+        return fn;
+}
+#        define SRC_FILE_CSTR(lineno) mw_perl_src_file_cstr(lineno)
+#endif /* MWRAP_PERL */
+
+#if MWRAP_RUBY
+static const char *mw_ruby_src_file_cstr(unsigned *lineno);
+#        define SRC_FILE_CSTR(lineno) mw_ruby_src_file_cstr(lineno)
+#endif /* MWRAP_RUBY */
+
+#ifndef SRC_FILE_CSTR /* for C-only compilation */
+#        define SRC_FILE_CSTR(lineno)        (NULL)
+#endif /* !SRC_FILE_CSTR */
+
+static struct src_loc *assign_line(size_t size, struct src_loc *sl,
+                                const char *fn, unsigned lineno)
+{
         struct src_file *f;
         union stk_sf sf;
         struct cds_lfht_node *cur;
@@ -430,7 +455,7 @@ again:
 }
 
 static struct src_loc *
-update_stats_rcu_lock(size_t *generation, size_t size, struct src_loc *sl)
+update_stats_rcu_lock(size_t *gen, size_t size, struct src_loc *sl)
 {
         struct cds_lfht *t = CMM_LOAD_SHARED(totals);
         struct src_loc *ret = NULL;
@@ -438,11 +463,15 @@ update_stats_rcu_lock(size_t *generation, size_t size, struct src_loc *sl)
         if (caa_unlikely(!t)) return 0; /* not initialized */
         if (locating++) goto out; /* do not recurse into another *alloc */
 
-        *generation = uatomic_add_return(&total_bytes_inc, size);
-        const COP *cop = mwp_curcop();
+        SET_GENERATION(gen, size);
+
+        unsigned lineno;
+        const char *fn = SRC_FILE_CSTR(&lineno);
+
         rcu_read_lock();
-        ret = assign_line(size, cop, sl);
-        if (!ret) { /* no associated Perl code, just C/C++ */
+        if (fn)
+                ret = assign_line(size, sl, fn, lineno);
+        if (!ret) { /* no associated Perl|Ruby code, just C/C++ */
                 sl->total = size;
                 sl->f = NULL;
                 sl->lineno = 0;
@@ -1012,3 +1041,54 @@ __attribute__((constructor)) static void mwrap_ctor(void)
         }
         --locating;
 }
+
+#if MWRAP_RUBY
+#        undef _GNU_SOURCE /* ruby.h redefines it */
+#        include <ruby.h> /* defines HAVE_RUBY_RACTOR_H on 3.0+ */
+#        include <ruby/thread.h>
+#        include <ruby/io.h>
+#        ifdef HAVE_RUBY_RACTOR_H /* Ruby 3.0+ */
+extern MWRAP_TSD void * __attribute__((weak)) ruby_current_ec;
+#        else /* Ruby 2.6-2.7 */
+extern void * __attribute__((weak)) ruby_current_execution_context_ptr;
+#                define ruby_current_ec ruby_current_execution_context_ptr
+#        endif /* HAVE_RUBY_RACTOR_H */
+
+extern void * __attribute__((weak)) ruby_current_vm_ptr; /* for rb_gc_count */
+extern size_t __attribute__((weak)) rb_gc_count(void);
+int __attribute__((weak)) ruby_thread_has_gvl_p(void);
+
+const char *rb_source_location_cstr(int *line); /* requires 2.6.0dev or later */
+/*
+ * rb_source_location_cstr relies on GET_EC(), and it's possible
+ * to have a native thread but no EC during the early and late
+ * (teardown) phases of the Ruby process
+ */
+static int has_ec_p(void)
+{
+        return ruby_thread_has_gvl_p && ruby_thread_has_gvl_p() &&
+                ruby_current_vm_ptr && ruby_current_ec;
+}
+
+static void mw_ruby_set_generation(size_t *gen, size_t size)
+{
+        if (rb_gc_count) {
+                uatomic_add_return(&total_bytes_inc, size);
+                if (has_ec_p()) {
+                        *gen = rb_gc_count();
+                        uatomic_set(&last_gc_count, *gen);
+                }
+        } else {
+                *gen = uatomic_add_return(&total_bytes_inc, size);
+        }
+}
+
+static const char *mw_ruby_src_file_cstr(unsigned *lineno)
+{
+        if (!has_ec_p()) return NULL;
+        int line;
+        const char *fn = rb_source_location_cstr(&line);
+        *lineno = line < 0 ? UINT_MAX : (unsigned)line;
+        return fn;
+}
+#endif /* !MWRAP_RUBY */