mwrap user+dev discussion/patches/pulls/bugs/help
 help / color / mirror / code / Atom feed
From: Eric Wong <e@80x24.org>
To: mwrap-public@80x24.org
Subject: [PATCH] documentation updates for 2.0.0 release
Date: Fri, 20 Jul 2018 09:11:49 +0000	[thread overview]
Message-ID: <20180720091149.18487-1-e@80x24.org> (raw)

---
 .document         |  1 +
 README            | 34 ++++++++++++++++++++++------------
 ext/mwrap/mwrap.c | 40 +++++++++++++++++++++++++++++++---------
 lib/mwrap_rack.rb | 26 +++++++++++++++++++++-----
 4 files changed, 75 insertions(+), 26 deletions(-)

diff --git a/.document b/.document
index c35f075..4ca33e3 100644
--- a/.document
+++ b/.document
@@ -1 +1,2 @@
 ext/mwrap/mwrap.c
+lib/mwrap_rack.rb
diff --git a/README b/README
index e3bcceb..3a20258 100644
--- a/README
+++ b/README
@@ -4,20 +4,23 @@ mwrap is designed to answer the question:
 
    Which lines of Ruby are hitting malloc the most?
 
-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.
+mwrap wraps all malloc-family calls to trace the Ruby source
+location of such calls and bytes allocated at each callsite.
+As of mwrap 2.0.0, it can also function as a leak detector
+and show live allocations at every call site.  Depending on
+your application and workload, the overhead is roughly a 50%
+increase memory and runtime.
+
+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/
 
-It does not require recompiling or rebuilding Ruby, but 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 (tested 11.1)
@@ -45,10 +48,12 @@ variable to append the results to a log file:
 	sort -k1,1rn </path/to/log | $PAGER
 
 You may also `require "mwrap"' in your Ruby code and use
-Mwrap.dump, Mwrap.clear, Mwrap.reset, Mwrap.each, etc.
+Mwrap.dump, Mwrap.reset, Mwrap.each, etc.
 
 However, mwrap MUST be loaded via LD_PRELOAD to have any
-effect in tracking malloc use.
+effect in tracking malloc use.  However, it is safe to keep
+"require 'mwrap'" in performance-critical deployments,
+as overhead is only incurred when used as an LD_PRELOAD.
 
 The output of the mwrap dump is a text file with 3 columns:
 
@@ -59,6 +64,11 @@ or an address retrieved by backtrace_symbols(3).  It is
 recommended to use the sort(1) command on either of the
 first two columns to find the hottest malloc locations.
 
+mwrap 2.0.0+ also supports a Rack application endpoint,
+it is documented at:
+
+	https://80x24.org/mwrap/MwrapRack.html
+
 == Known problems
 
 * 32-bit machines are prone to overflow (WONTFIX)
diff --git a/ext/mwrap/mwrap.c b/ext/mwrap/mwrap.c
index c1a59e7..acc8960 100644
--- a/ext/mwrap/mwrap.c
+++ b/ext/mwrap/mwrap.c
@@ -942,6 +942,8 @@ static struct src_loc *src_loc_get(VALUE self)
  * yielding the +size+ (in bytes) and +generation+ of each allocation.
  * The +generation+ is the value of the GC.count method at the time
  * the allocation was made.
+ *
+ * This functionality is only available in mwrap 2.0.0+
  */
 static VALUE src_loc_each(VALUE self)
 {
@@ -954,6 +956,10 @@ static VALUE src_loc_each(VALUE self)
 	return self;
 }
 
+/*
+ * The the mean lifespan (in GC generations) of allocations made from this
+ * location.  This does not account for live allocations.
+ */
 static VALUE src_loc_mean_lifespan(VALUE self)
 {
 	struct src_loc *l = src_loc_get(self);
@@ -964,21 +970,28 @@ static VALUE src_loc_mean_lifespan(VALUE self)
 	return DBL2NUM(frees ? ((double)tot/(double)frees) : HUGE_VAL);
 }
 
+/* The number of frees made from this location */
 static VALUE src_loc_frees(VALUE self)
 {
 	return SIZET2NUM(uatomic_read(&src_loc_get(self)->frees));
 }
 
+/* The number of allocations made from this location */
 static VALUE src_loc_allocations(VALUE self)
 {
 	return SIZET2NUM(uatomic_read(&src_loc_get(self)->allocations));
 }
 
+/* The total number of bytes allocated from this location */
 static VALUE src_loc_total(VALUE self)
 {
 	return SIZET2NUM(uatomic_read(&src_loc_get(self)->total));
 }
 
+/*
+ * The maximum age (in GC generations) of an allocation before it was freed.
+ * This does not account for live allocations.
+ */
 static VALUE src_loc_max_lifespan(VALUE self)
 {
 	return SIZET2NUM(uatomic_read(&src_loc_get(self)->max_lifespan));
@@ -1011,6 +1024,8 @@ static VALUE reset_locating(VALUE ign) { --locating; return Qfalse; }
  * Stops allocation tracking inside the block.  This is useful for
  * monitoring code which calls other Mwrap (or ObjectSpace/GC)
  * functions which unavoidably allocate memory.
+ *
+ * This feature was added in mwrap 2.0.0+
  */
 static VALUE mwrap_quiet(VALUE mod)
 {
@@ -1052,9 +1067,16 @@ void Init_mwrap(void)
 	VALUE mod;
 
 	++locating;
-        mod = rb_define_module("Mwrap");
+	mod = rb_define_module("Mwrap");
 	id_uminus = rb_intern("-@");
 
+	/*
+	 * Represents a location in source code or library
+	 * address which calls a memory allocation.  It is
+	 * updated automatically as allocations are made, so
+	 * there is no need to reload or reread it from Mwrap#[].
+	 * This class is only available since mwrap 2.0.0+.
+	 */
 	cSrcLoc = rb_define_class_under(mod, "SourceLocation", rb_cObject);
 	rb_define_singleton_method(mod, "dump", mwrap_dump, -1);
 	rb_define_singleton_method(mod, "reset", mwrap_reset, 0);
@@ -1080,18 +1102,18 @@ void Init_mwrap(void)
 __attribute__ ((destructor))
 static void mwrap_dump_destructor(void)
 {
-        const char *opt = getenv("MWRAP");
-        const char *modes[] = { "a", "a+", "w", "w+", "r+" };
-        struct dump_arg a;
-        size_t i;
-        int dump_fd;
+	const char *opt = getenv("MWRAP");
+	const char *modes[] = { "a", "a+", "w", "w+", "r+" };
+	struct dump_arg a;
+	size_t i;
+	int dump_fd;
 	char *dump_path;
 
 	if (!opt)
 		return;
 
-        ++locating;
-        if ((dump_path = strstr(opt, "dump_path:")) &&
+	++locating;
+	if ((dump_path = strstr(opt, "dump_path:")) &&
 			(dump_path += sizeof("dump_path")) &&
 			*dump_path) {
 		char *end = strchr(dump_path, ',');
@@ -1136,5 +1158,5 @@ static void mwrap_dump_destructor(void)
 	}
 	dump_to_file(&a);
 out:
-    --locating;
+	--locating;
 }
diff --git a/lib/mwrap_rack.rb b/lib/mwrap_rack.rb
index ef3872b..a750f32 100644
--- a/lib/mwrap_rack.rb
+++ b/lib/mwrap_rack.rb
@@ -5,9 +5,24 @@ require 'mwrap'
 require 'rack'
 require 'cgi'
 
-# Usage: mwrap rackup ...
+# MwrapRack is a standalone Rack application which can be
+# mounted to run within your application process.
+#
+# Using the Rack::Builder API in config.ru, you can map it to
+# the "/MWRAP/" endpoint.  As with the rest of the Mwrap API,
+# your Rack server needs to be spawned with the mwrap(1)
+# wrapper to enable the LD_PRELOAD.
+#
+#     require 'mwrap_rack'
+#     map('/MWRAP') { run(MwrapRack.new) }
+#     map('/') { run(your_normal_app) }
+#
+# A live demo is available at https://80x24.org/MWRAP/
+# (warning the demo machine is 32-bit, so counters will overflow)
+#
+# This module is only available in mwrap 2.0.0+
 class MwrapRack
-  module HtmlResponse
+  module HtmlResponse # :nodoc:
     def response
       [ 200, {
           'Expires' => 'Fri, 01 Jan 1980 00:00:00 GMT',
@@ -18,7 +33,7 @@ class MwrapRack
     end
   end
 
-  class Each < Struct.new(:script_name, :min, :sort)
+  class Each < Struct.new(:script_name, :min, :sort) # :nodoc:
     include HtmlResponse
     HEADER = '<tr><th>' + %w(total allocations frees mean_life max_life
                 location).join('</th><th>') + '</th></tr>'
@@ -61,7 +76,7 @@ class MwrapRack
     end
   end
 
-  class EachAt < Struct.new(:loc)
+  class EachAt < Struct.new(:loc) # :nodoc:
     include HtmlResponse
     HEADER = '<tr><th>size</th><th>generation</th></tr>'
 
@@ -77,10 +92,11 @@ class MwrapRack
     end
   end
 
-  def r404
+  def r404 # :nodoc:
     [404,{'Content-Type'=>'text/plain'},["Not found\n"]]
   end
 
+  # The standard Rack application endpoint for MwrapRack
   def call(env)
     case env['PATH_INFO']
     when %r{\A/each/(\d+)\z}
-- 
EW


                 reply	other threads:[~2018-07-20  9:11 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://80x24.org/mwrap/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20180720091149.18487-1-e@80x24.org \
    --to=e@80x24.org \
    --cc=mwrap-public@80x24.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://80x24.org/mwrap.git/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).