about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <e@80x24.org>2018-07-02 09:26:22 +0000
committerEric Wong <e@80x24.org>2018-07-02 09:26:22 +0000
commit61ee1c46a06268cbcf5e7d916a05ca9091337f7a (patch)
treed794c923df8fb6cf661b8c1e98911846af6a4c20
parenta47e497fbc2322457ac18679f9046250d4ee9d8f (diff)
downloadmwrap-61ee1c46a06268cbcf5e7d916a05ca9091337f7a.tar.gz
-rw-r--r--.document1
-rw-r--r--.gitignore1
-rw-r--r--README42
-rw-r--r--ext/mwrap/mwrap.c62
4 files changed, 91 insertions, 15 deletions
diff --git a/.document b/.document
new file mode 100644
index 0000000..c35f075
--- /dev/null
+++ b/.document
@@ -0,0 +1 @@
+ext/mwrap/mwrap.c
diff --git a/.gitignore b/.gitignore
index 851644a..aa3606c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,4 @@
 *.so
 /pkg
 /*.gem
+/doc
diff --git a/README b/README
index ed4ea08..f9409b0 100644
--- a/README
+++ b/README
@@ -1,21 +1,28 @@
 = mwrap - LD_PRELOAD malloc wrapper + line stats for Ruby
 
-Wraps all malloc, calloc, and realloc calls to trace the Ruby source
-location of such calls and bytes allocated at each callsite.  This
-functionality may change incompatibly or be expanded in the future.
+mwrap is designed to answer the question:
 
-This is useful for finding malloc hotspots in Ruby code.  It
-does not track allocation lifetimes, or frees, however.  It
-works best for allocations under GVL, but tries to track numeric
-caller addresses for allocations made without GVL.
+   Which lines of Ruby are hitting malloc the most?
 
-It requires a concurrent lock-free hash table from the
+mwrap wraps all malloc, calloc, and realloc calls to trace the Ruby
+source location of such calls and bytes allocated at each callsite.
+This functionality may be expanded in the future.
+
+It does not track allocation lifetimes, or frees, however.  It works
+best for allocations under GVL, but tries to track numeric caller
+addresses for allocations made without GVL so you can get an idea of how
+much memory usage certain extensions and native libraries use.
+
+It requires the concurrent lock-free hash table from the
 Userspace RCU project: https://liburcu.org/
 
-Only supports Ruby trunk (2.6.0dev+) on a few platforms:
+It does not require recompiling or rebuilding Ruby, but only supports
+Ruby trunk (2.6.0dev+) on a few platforms:
 
 * GNU/Linux
-* FreeBSD 11
+* FreeBSD (tested 11.1)
+
+It may work on NetBSD, OpenBSD and DragonFly BSD.
 
 == Install
 
@@ -40,10 +47,17 @@ You may also set dump_path to append to a log file:
         MWRAP=dump_path:/path/to/log mwrap RUBY_COMMAND
 
 You may also `require 'mwrap'' in your Ruby code and use
-Mwrap.dump, Mwrap.clear, Mwrap.each, etc.
+Mwrap.dump, Mwrap.clear, Mwrap.reset, Mwrap.each, etc.
+
+However, mwrap MUST be loaded via LD_PRELOAD to have any
+effect in tracking malloc use.
 
-However, mwrap MUST be loaded via LD_PRELOAD-ed to have any
-effect in tracking mallocs.
+The output of the mwrap dump is a text file with 3 columns:
+
+        total_bytes        call_count        location
+
+Where location is a Ruby source location (if made under GVL)
+or an address retrieved by backtrace_symbols(3)
 
 == Known problems
 
@@ -70,5 +84,3 @@ systems.
 == License
 
 GPL-2.0+ <https://www.gnu.org/licenses/gpl-2.0.txt>
-
-Note: we may depend on 3rd-party LGPL/GPL libraries in future releases
diff --git a/ext/mwrap/mwrap.c b/ext/mwrap/mwrap.c
index fbba32b..e6449d7 100644
--- a/ext/mwrap/mwrap.c
+++ b/ext/mwrap/mwrap.c
@@ -323,6 +323,19 @@ out_unlock:
         return 0;
 }
 
+/*
+ * call-seq:
+ *
+ *        Mwrap.dump([[io] [, min]] -> nil
+ *
+ * Dumps the current totals to +io+ which must be an IO object
+ * (StringIO and similar are not supported).  Total sizes smaller
+ * than or equal to +min+ are skipped.
+ *
+ * The output is space-delimited by 3 columns:
+ *
+ * total_size      call_count      location
+ */
 static VALUE mwrap_dump(int argc, VALUE * argv, VALUE mod)
 {
         VALUE io, min;
@@ -373,6 +386,15 @@ static void *totals_clear(void *ign)
         return 0;
 }
 
+/*
+ * call-seq:
+ *
+ *        Mwrap.clear -> nil
+ *
+ * Atomically replaces the totals table and destroys the old one.
+ * This resets all statistics. It is more expensive than `Mwrap.reset'
+ * as new allocations will need to be made to repopulate the new table.
+ */
 static VALUE mwrap_clear(VALUE mod)
 {
         rb_thread_call_without_gvl(totals_clear, 0, 0, 0);
@@ -395,6 +417,15 @@ static void *totals_reset(void *ign)
         return 0;
 }
 
+/*
+ * call-seq:
+ *
+ *        Mwrap.reset -> nil
+ *
+ * Resets the the total tables by zero-ing all counters.
+ * This resets all statistics and is less costly than `Mwrap.clear'
+ * but is not an atomic operation.
+ */
 static VALUE mwrap_reset(VALUE mod)
 {
         rb_thread_call_without_gvl(totals_reset, 0, 0, 0);
@@ -443,6 +474,15 @@ static VALUE dump_each_rcu(VALUE x)
         return Qnil;
 }
 
+/*
+ * call-seq:
+ *
+ *         Mwrap.each([min]) { |location,total_bytes,call_count| ... }
+ *
+ * Yields each entry of the of the table to a caller-supplied block.
+ * +min+ may be specified to filter out lines with +total_bytes+
+ * equal-to-or-smaller-than the supplied minimum.
+ */
 static VALUE mwrap_each(int argc, VALUE * argv, VALUE mod)
 {
         VALUE min;
@@ -457,6 +497,28 @@ static VALUE mwrap_each(int argc, VALUE * argv, VALUE mod)
         return rb_ensure(dump_each_rcu, (VALUE)&a, dump_ensure, 0);
 }
 
+/*
+ * Document-module: Mwrap
+ *
+ *   require 'mwrap'
+ *
+ * Mwrap has a dual function as both a Ruby C extension and LD_PRELOAD
+ * wrapper.  As a Ruby C extension, it exposes a limited Ruby API.
+ * To be effective at gathering status, mwrap must be loaded as a
+ * LD_PRELOAD (using the mwrap(1) executable makes it easy)
+ *
+ * ENVIRONMENT
+ *
+ * The "MWRAP" environment variable contains a comma-delimited list
+ * of key:value options for automatically dumping at program exit.
+ *
+ * * dump_fd: a writable FD to dump to
+ * * dump_path: a path to dump to, the file is opened in O_APPEND mode
+ * * dump_min: the minimum allocation size (total) to dump
+ *
+ * If both `dump_fd' and `dump_path' are specified, dump_path takes
+ * precedence.
+ */
 void Init_mwrap(void)
 {
         VALUE mod = rb_define_module("Mwrap");