mwrap user+dev discussion/patches/pulls/bugs/help
 help / color / mirror / code / Atom feed
Search results ordered by [date|relevance]  view[summary|nested|Atom feed]
thread overview below | download mbox.gz: |
* [PATCH] various doc updates, add mwrap(1) manpage
@ 2023-01-09  5:52  6% Eric Wong
  0 siblings, 0 replies; 10+ results
From: Eric Wong @ 2023-01-09  5:52 UTC (permalink / raw)
  To: mwrap-public

FreeBSD 12.x doesn't seem to work with the `pkg install'-ed
ruby
---
 Documentation/.gitignore  |   2 +
 Documentation/GNUmakefile |  63 +++++++++++++++++++
 Documentation/mwrap.pod   | 123 ++++++++++++++++++++++++++++++++++++++
 MANIFEST                  |   4 +-
 README                    |  38 +++++++-----
 mwrap.gemspec             |  11 +++-
 6 files changed, 223 insertions(+), 18 deletions(-)
 create mode 100644 Documentation/.gitignore
 create mode 100644 Documentation/GNUmakefile
 create mode 100644 Documentation/mwrap.pod

diff --git a/Documentation/.gitignore b/Documentation/.gitignore
new file mode 100644
index 0000000..1b0e502
--- /dev/null
+++ b/Documentation/.gitignore
@@ -0,0 +1,2 @@
+mwrap.txt
+mwrap.1
diff --git a/Documentation/GNUmakefile b/Documentation/GNUmakefile
new file mode 100644
index 0000000..14480da
--- /dev/null
+++ b/Documentation/GNUmakefile
@@ -0,0 +1,63 @@
+# Copyright (C) all contributors <mwrap-public@80x24.org>
+# License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
+all::
+
+INSTALL = install
+POD2MAN = pod2man
+VERSION := $(shell cd .. && ./VERSION-GEN)
+release := mwrap $(VERSION)
+POD2MAN_OPTS = -v -r '$(release)' --stderr -d 1993-10-02 -c 'mwrap user manual'
+pod2man = $(POD2MAN) $(POD2MAN_OPTS)
+POD2TEXT = pod2text
+POD2TEXT_OPTS = --stderr
+pod2text = $(POD2TEXT) $(POD2TEXT_OPTS)
+
+m1 = mwrap
+m5 =
+m7 =
+
+man1 := $(addsuffix .1, $(m1))
+man5 := $(addsuffix .5, $(m5))
+man7 := $(addsuffix .7, $(m7))
+
+all:: man
+
+man: $(man1) $(man5) $(man7)
+
+prefix ?= $(HOME)
+mandir ?= $(prefix)/share/man
+man1dir = $(mandir)/man1
+man5dir = $(mandir)/man5
+man7dir = $(mandir)/man7
+
+gem-man: man
+	$(INSTALL) -d -m 755 ../man
+	test -z "$(man1)" || $(INSTALL) -m 644 $(man1) ../man
+	test -z "$(man5)" || $(INSTALL) -m 644 $(man5) ../man
+	test -z "$(man7)" || $(INSTALL) -m 644 $(man7) ../man
+
+install-man: man
+	$(INSTALL) -d -m 755 $(DESTDIR)$(man1dir)
+	$(INSTALL) -d -m 755 $(DESTDIR)$(man5dir)
+	test -z "$(man7)" || $(INSTALL) -d -m 755 $(DESTDIR)$(man7dir)
+	$(INSTALL) -m 644 $(man1) $(DESTDIR)$(man1dir)
+	$(INSTALL) -m 644 $(man5) $(DESTDIR)$(man5dir)
+	test -z "$(man7)" || $(INSTALL) -m 644 $(man7) $(DESTDIR)$(man7dir)
+
+%.1 %.5 %.7 : %.pod
+	$(pod2man) -s $(subst .,,$(suffix $@)) $< $@+ && mv $@+ $@
+
+mantxt = $(addsuffix .txt, $(m1) $(m5) $(m7))
+
+txt :: $(mantxt)
+
+all :: txt
+
+%.txt : %.pod
+	$(pod2text) $< $@+
+	touch -r $< $@+
+	mv $@+ $@
+
+clean::
+	$(RM) $(man1) $(man5) $(man7)
+	$(RM) $(addsuffix .txt.gz, $(m1) $(m5) $(m7))
diff --git a/Documentation/mwrap.pod b/Documentation/mwrap.pod
new file mode 100644
index 0000000..6832430
--- /dev/null
+++ b/Documentation/mwrap.pod
@@ -0,0 +1,123 @@
+=head1 NAME
+
+mwrap - run any command under mwrap
+
+=head1 SYNOPSIS
+
+  # to trace a long-running program and access it via $DIR/$PID.sock:
+  MWRAP=socket_dir:$DIR mwrap COMMAND
+
+  # to trace a short-lived command and dump its output to a log:
+  MWRAP=dump_path:$FILENAME mwrap COMMAND
+
+=head1 DESCRIPTION
+
+mwrap is a command-line to automatically add mwrap.so as an
+LD_PRELOAD for any command.  It will resolve malloc-family calls
+to a Ruby file and line number, and it can also provide a backtrace
+of native (C/C++) functions for non-Ruby programs.
+
+=head1 ENVIRONMENT
+
+C<MWRAP> is the only environment variable read.  It contains multiple
+options delimited by C<,> with names and values delimited by C<:>
+
+=over 4
+
+=item socket_dir:$DIR
+
+This launches an embedded HTTP server in each process and binds it
+to C<$DIR/$PID.sock>.  C<curl --unix-socket $DIR/$PID.sock>
+or L<mwrap-rproxy(1p)> (from L<https://80x24.org/mwrap-perl.git>) may
+be used to access various endpoints in the HTTP server.
+
+=item bt:$DEPTH
+
+The backtrace depth for L<backtrace(3)> in addition to the Perl
+file and line number where C<$DEPTH> is a non-negative number.
+
+The maximum allowed value is 32, though values of 5 or less are
+typically useful.  Increasing this to even 2 or 3 can significantly
+increase the amount of memory mwrap (and liburcu) itself uses.
+
+This is only useful in conjunction with C<socket_dir>
+
+This may be changed via POST request (see below).
+
+Default: 0
+
+=item dump_path:$FILENAME
+
+Dumps the output at exit to a given filename:
+
+	total_bytes	call_count	location
+
+In the future, dumping to a self-describing CSV will be supported.
+
+=item dump_fd:$DESCRIPTOR
+
+As with dump_path, but dumps the output to a given file descriptor.
+
+=back
+
+=head1 HTTP POST API
+
+In addition to the various GET endpoints linked via C<http://0/$PID/>,
+there are some POST endpoints which are typically accessed via
+C<curl --unix-socket $DIR/$PID.sock>
+
+=over 4
+
+=item POST http://0/$PID/reset
+
+C<curl --unix-socket $DIR/$PID.sock -XPOST http://0/$PID/reset>
+
+Reset all internal counters.  This is not done atomically and does
+not release any memory.
+
+=item POST http://0/$PID/trim
+
+C<curl --unix-socket $DIR/$PID.sock -XPOST http://0/$PID/trim>
+
+Runs L<malloc_trim(3)> with a 0 pad value to release unused memory
+back to the kernel.  In our malloc implementation, this is done
+lazily to avoid contention and does not happen unless sleeping threads.
+
+=item POST http://0/$PID/ctl
+
+Set various internal knobs.  Currently, C<X-Mwrap-BT> is the
+only knob supported:
+
+C<curl --unix-socket $DIR/$PID.sock -XPOST -HX-Mwrap-BT:1 http://0/$PID/ctl>
+
+Using the C<X-Mwrap-BT> header allows changing the aforementioned
+C<bt:> value to a specified depth level.  As with C<bt:>, only make small
+adjustments as the memory cost can increase exponentially with each step.
+
+It is typically a good idea to reset (C<http://0/$PID/reset>) after changing
+the depth on a running process.
+
+Headers other than C<X-Mwrap-BT> may be accepted in the future to
+tweak other settings.
+
+=back
+
+=head1 CONTACT
+
+Feedback welcome via plain-text mail to L<mailto:mwrap-public@80x24.org>
+
+Mail archives are hosted at L<https://80x24.org/mwrap-public/>
+
+=head1 COPYRIGHT
+
+Copyright all contributors L<mailto:mwrap-public@80x24.org>
+
+License: GPL-3.0+ L<https://www.gnu.org/licenses/gpl-3.0.txt>
+
+Source code is at L<https://80x24.org/mwrap.git/>
+
+=head1 SEE ALSO
+
+L<mwrap-rproxy(1)>, L<Devel::Mwrap(3pm)>, L<https://80x24.org/mwrap-perl.git>
+
+=cut
diff --git a/MANIFEST b/MANIFEST
index 4c5be8b..8d4cdd6 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -2,6 +2,9 @@
 .gitignore
 .olddoc.yml
 COPYING
+Documentation/.gitignore
+Documentation/GNUmakefile
+Documentation/mwrap.pod
 MANIFEST
 README
 Rakefile
@@ -24,4 +27,3 @@ mwrap.gemspec
 t/httpd.t
 t/test_common.perl
 test/test_mwrap.rb
-lib/mwrap/version.rb
diff --git a/README b/README
index 382c5a0..761f87e 100644
--- a/README
+++ b/README
@@ -8,8 +8,8 @@ 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.
+your application and workload, the overhead is roughly a 50-100%
+increase memory and runtime with default settings.
 
 It works best for allocations under GVL, but tries to track
 numeric caller addresses for allocations made without GVL so you
@@ -22,15 +22,13 @@ Userspace RCU project: https://liburcu.org/
 It does not require recompiling or rebuilding Ruby, but only
 supports Ruby 2.7.0 or later on a few platforms:
 
-* GNU/Linux
-* FreeBSD
+* GNU/Linux (only tested --without-jemalloc, mwrap 3.x provides its own)
 
-It may work on NetBSD, OpenBSD and DragonFly BSD.
+It may work on FreeBSD, NetBSD, OpenBSD and DragonFly BSD if given
+appropriate build options.
 
 == Install
 
-	# FreeBSD: pkg install liburcu
-
 	# Debian-based systems: apt-get liburcu-dev
 
 	# Install mwrap via RubyGems.org
@@ -64,24 +62,33 @@ 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,
+mwrap 3.0.0+ also supports an embedded HTTP server
 it is documented at:
 
-https://80x24.org/mwrap/MwrapRack.html
+https://80x24.org/mwrap.git/tree/Documentation/mwrap.pod
 
 == Known problems
 
 * 32-bit machines are prone to overflow (WONTFIX)
 
-== Public mail archives and contact info:
+* signalfd(2)-reliant code will need latest URCU with commit
+  ea3a28a3f71dd02f (Disable signals in URCU background threads, 2022-09-23)
+  This doesn't affect C Ruby itself, and signalfd(2) use is rare
+  3rd-party processes.
+
+* Ruby source files over 16.7 million lines long are not supported :P
+
+== Public mail archives (HTTP, Atom feeds, IMAP mailbox, NNTP group, POP3):
 
 * https://80x24.org/mwrap-public/
 * nntps://80x24.org/inbox.comp.lang.ruby.mwrap
 * imaps://;AUTH=ANONYMOUS@80x24.org/inbox.comp.lang.ruby.mwrap.0
 * https://80x24.org/mwrap-public/_/text/help/#pop3
 
-No subscription will ever be required to post, but HTML mail
-will be rejected:
+No subscription nor real identities will ever be required to obtain support,
+but HTML mail is rejected.  Memory usage reductions start with you;
+only send plain-text mail to us and do not top-post.  HTML mail and
+top-posting costs everybody memory and bandwidth.
 
 		mwrap-public@80x24.org
 
@@ -89,9 +96,10 @@ will be rejected:
 
 	git clone https://80x24.org/mwrap.git
 
-Send all patches and pull requests (use "git request-pull" to format) to
-mwrap-public@80x24.org.  We do not use centralized or proprietary messaging
-systems.
+Send all patches ("git format-patch" + "git send-email") and
+pull requests (use "git request-pull" to format) via email
+to mwrap-perl@80x24.org.  We do not and will not use
+proprietary messaging systems.
 
 == License
 
diff --git a/mwrap.gemspec b/mwrap.gemspec
index dc99924..b6f9e71 100644
--- a/mwrap.gemspec
+++ b/mwrap.gemspec
@@ -1,6 +1,5 @@
 git_manifest = `git ls-files 2>/dev/null`.split("\n")
 git_ok = $?.success?
-git_manifest << 'lib/mwrap/version.rb'.freeze # generated by ./VERSION-GEN
 manifest = File.exist?('MANIFEST') ?
   File.readlines('MANIFEST').map!(&:chomp).delete_if(&:empty?) : git_manifest
 if git_ok && manifest != git_manifest
@@ -12,11 +11,19 @@ end
 
 version = `./VERSION-GEN`.chomp
 $?.success? or abort './VERSION-GEN failed'
+manifest << 'lib/mwrap/version.rb'.freeze
+
+if system(*%w(make -C Documentation man)) ||
+   system(*%w(gmake -C Documentation man))
+  manifest.concat(%w(Documentation/mwrap.1))
+else
+  warn 'failed to build man-page(s), proceeding without them'
+end
 
 Gem::Specification.new do |s|
   s.name = 'mwrap'
   s.version = version
-  s.homepage = 'https://80x24.org/mwrap/'
+  s.homepage = 'https://80x24.org/mwrap.git/'
   s.authors = ["mwrap hackers"]
   s.summary = 'LD_PRELOAD malloc wrapper for Ruby'
   s.executables = %w(mwrap)

^ permalink raw reply related	[relevance 6%]

* [ANN] mwrap 2.1.0 mwrap - LD_PRELOAD malloc wrapper for Ruby
@ 2018-08-11  4:31  6% Eric Wong
  0 siblings, 0 replies; 10+ results
From: Eric Wong @ 2018-08-11  4:31 UTC (permalink / raw)
  To: ruby-talk, mwrap-public

Changes:

    mwrap 2.1.0 - heap_page_body struct tracking

    This release enables tracking of memalign allocations for
    "struct heap_page_body" in the Ruby GC.  This can be useful
    for tracking deathspans (time between free and re-allocation)
    of heap page bodies which can cause fragmentation in some
    malloc implementations, including glibc.

    The documentation for it is available at:

      https://80x24.org/mwrap/Mwrap/HeapPageBody.html

    And a live demo runs at:

      https://80x24.org/MWRAP/heap_pages

    This release also includes global counters for
     Mwrap.total_bytes_allocated and Mwrap.total_bytes_freed

    10 changes since v2.0.0 (2018-07-20):

          add olddoc.yml to generate links in page footers
          add .olddoc.yml to MANIFEST
          gemspec: use "git describe" output for prereleases
          add global counters for total bytes allocated/freed
          keep stats for memalign-ed heap_page_body in Ruby
          remove "memalign:" MWRAP option
          allow dump_heap: mask via MWRAP env
          tweak hpb stats destructor output
          struct acc: use 64-bit counters
          doc: 2.1 pre-release updates

About:

mwrap is designed to answer the question:

   Which lines of Ruby are hitting malloc the most?

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:

* GNU/Linux
* FreeBSD (tested 11.1)

It may work on NetBSD, OpenBSD and DragonFly BSD.

Mailing list and archives:

	https://80x24.org/mwrap-public/
	nntp://80x24.org/inbox.comp.lang.ruby.mwrap
	mailto:mwrap-public@80x24.org (no HTML mail, please)

Note: I might not be able answer questions about this for a few days.

git clone https://80x24.org/mwrap.git
homepage + rdoc: https://80x24.org/mwrap/

^ permalink raw reply	[relevance 6%]

* Re: [ANN] mwrap 2.0.0 mwrap - LD_PRELOAD malloc wrapper for Ruby
  2018-07-26  5:02  6%         ` Sam Saffron
@ 2018-07-26  6:21  6%           ` Eric Wong
  0 siblings, 0 replies; 10+ results
From: Eric Wong @ 2018-07-26  6:21 UTC (permalink / raw)
  To: Sam Saffron; +Cc: ruby-talk, mwrap-public

Sam Saffron <sam.saffron@gmail.com> wrote:
> Yes, this patch looks right to me.

OK, I've just pushed it out to RubyGems.org as a prerelease:

    mwrap-2.0.0.4.gd1ea.gem

> Even if we don't have perfect fidelity here it will give absolute
> clarity on "leak" vs "fragmentation related bloat". Even though
> jemalloc tries hard to compensate for fragmentation bloat is still
> possible.

Just wondering, are you still on jemalloc 3.6.0 or one of the
newer versions?  I seem to remember 3.6.0 interacting badly with
cross-thread frees (from another project years ago); and mwrap
relies on call_rcu to free memory which is in another thread...

Maybe narenas:1 or even using MALLOC_ARENA_MAX=1 glibc malloc
might make it easier to discern a real leak from fragmentation.

In any case; one technique I've used in the past which never
required special debugging tools (aside from source access) was
to use a bisection search over the code path.  I disabled/skipped
over half the remaining code until the leak could no longer be
reproduced to narrow down where it did happen.

^ permalink raw reply	[relevance 6%]

* Re: [ANN] mwrap 2.0.0 mwrap - LD_PRELOAD malloc wrapper for Ruby
  2018-07-26  2:46  5%       ` Eric Wong
@ 2018-07-26  5:02  6%         ` Sam Saffron
  2018-07-26  6:21  6%           ` Eric Wong
  0 siblings, 1 reply; 10+ results
From: Sam Saffron @ 2018-07-26  5:02 UTC (permalink / raw)
  To: Eric Wong; +Cc: Ruby users, mwrap-public

Yes, this patch looks right to me.

Even if we don't have perfect fidelity here it will give absolute
clarity on "leak" vs "fragmentation related bloat". Even though
jemalloc tries hard to compensate for fragmentation bloat is still
possible.

For full context here is a dump when I started the process (it was 500meg rss)

https://transfer.sh/Q9zQS/start.txt

Here is how it looks now (1.2G rss):

https://transfer.sh/14fokY/now.txt


The script I use to generate this stuff is:

https://github.com/discourse/discourse/blob/master/script/mwrap_sidekiq

^ permalink raw reply	[relevance 6%]

* Re: [ANN] mwrap 2.0.0 mwrap - LD_PRELOAD malloc wrapper for Ruby
  2018-07-26  1:37  6%     ` Sam Saffron
@ 2018-07-26  2:46  5%       ` Eric Wong
  2018-07-26  5:02  6%         ` Sam Saffron
  0 siblings, 1 reply; 10+ results
From: Eric Wong @ 2018-07-26  2:46 UTC (permalink / raw)
  To: Sam Saffron; +Cc: ruby-talk, mwrap-public

Sam Saffron <sam.saffron@gmail.com> wrote:
> Just to clarify here, I mean 2 single global totals, not a per row
> kind of thing.
> 
> On Thu, Jul 26, 2018 at 11:36 AM, Sam Saffron <sam.saffron@gmail.com> wrote:
> > I am using mwrap to debug a little leak at the moment, one feature
> > request I do have though is a tally of totals.
> >
> > It would be nice if it could keep track of total allocated and total
> > released. That way if my RSS is bloating I can tell if it is due to
> > fragmentation or if it is due to a genuine leak really quick.

Something like the patch below?  (Barely tested)

Since mwrap doesn't track its own memory usage; this might be
useful if you have a lot of cold code paths doing allocations,
since RSS might not stabilize quickly in that case.

Also, if there's a leaker using a malloc wrapper like Ruby's
xmalloc (e.g.  https://bugs.ruby-lang.org/issues/14929 ) ; mwrap
won't make it easy to track down since it can only safely see
the one level up the call stack (using GCC's __builtin_return_address
with a non-zero level isn't safe)


diff --git a/ext/mwrap/mwrap.c b/ext/mwrap/mwrap.c
index acc8960..9bb44d0 100644
--- a/ext/mwrap/mwrap.c
+++ b/ext/mwrap/mwrap.c
@@ -32,6 +32,8 @@ extern size_t __attribute__((weak)) rb_gc_count(void);
 extern VALUE __attribute__((weak)) rb_cObject;
 extern VALUE __attribute__((weak)) rb_yield(VALUE);
 
+static size_t total_bytes_inc, total_bytes_dec;
+
 /* true for glibc/dlmalloc/ptmalloc, not sure about jemalloc */
 #define ASSUMED_MALLOC_ALIGNMENT (sizeof(void *) * 2)
 
@@ -327,6 +329,8 @@ static struct src_loc *update_stats_rcu_lock(size_t size, uintptr_t caller)
 	if (caa_unlikely(!totals)) return 0;
 	if (locating++) goto out; /* do not recurse into another *alloc */
 
+	uatomic_add(&total_bytes_inc, size);
+
 	rcu_read_lock();
 	if (has_ec_p()) {
 		int line;
@@ -390,6 +394,7 @@ void free(void *p)
 		if (l) {
 			size_t age = generation - h->as.live.gen;
 
+			uatomic_add(&total_bytes_dec, h->size);
 			uatomic_set(&h->size, 0);
 			uatomic_add(&l->frees, 1);
 			uatomic_add(&l->age_total, age);
@@ -710,12 +715,16 @@ static VALUE mwrap_dump(int argc, VALUE * argv, VALUE mod)
 	return Qnil;
 }
 
+/* The whole operation is not remotely atomic... */
 static void *totals_reset(void *ign)
 {
 	struct cds_lfht *t;
 	struct cds_lfht_iter iter;
 	struct src_loc *l;
 
+	uatomic_set(&total_bytes_inc, 0);
+	uatomic_set(&total_bytes_dec, 0);
+
 	rcu_read_lock();
 	t = rcu_dereference(totals);
 	cds_lfht_for_each_entry(t, &iter, l, hnode) {
@@ -1033,6 +1042,16 @@ static VALUE mwrap_quiet(VALUE mod)
 	return rb_ensure(rb_yield, SIZET2NUM(cur), reset_locating, 0);
 }
 
+static VALUE total_inc(VALUE mod)
+{
+	return SIZET2NUM(total_bytes_inc);
+}
+
+static VALUE total_dec(VALUE mod)
+{
+	return SIZET2NUM(total_bytes_dec);
+}
+
 /*
  * Document-module: Mwrap
  *
@@ -1084,6 +1103,8 @@ void Init_mwrap(void)
 	rb_define_singleton_method(mod, "each", mwrap_each, -1);
 	rb_define_singleton_method(mod, "[]", mwrap_aref, 1);
 	rb_define_singleton_method(mod, "quiet", mwrap_quiet, 0);
+	rb_define_singleton_method(mod, "total_bytes_allocated", total_inc, 0);
+	rb_define_singleton_method(mod, "total_bytes_freed", total_dec, 0);
 	rb_define_method(cSrcLoc, "each", src_loc_each, 0);
 	rb_define_method(cSrcLoc, "frees", src_loc_frees, 0);
 	rb_define_method(cSrcLoc, "allocations", src_loc_allocations, 0);
diff --git a/test/test_mwrap.rb b/test/test_mwrap.rb
index 8425c35..d112b4e 100644
--- a/test/test_mwrap.rb
+++ b/test/test_mwrap.rb
@@ -272,4 +272,15 @@ class TestMwrap < Test::Unit::TestCase
       res == :foo or abort 'Mwrap.quiet did not return block result'
     end;
   end
+
+  def test_total_bytes
+    assert_separately(+"#{<<~"begin;"}\n#{<<~'end;'}")
+    begin;
+      require 'mwrap'
+      Mwrap.total_bytes_allocated > 0 or abort 'nothing allocated'
+      Mwrap.total_bytes_freed > 0 or abort 'nothing freed'
+      Mwrap.total_bytes_allocated > Mwrap.total_bytes_freed or
+        abort 'freed more than allocated'
+    end;
+  end
 end

^ permalink raw reply related	[relevance 5%]

* Re: [ANN] mwrap 2.0.0 mwrap - LD_PRELOAD malloc wrapper for Ruby
  2018-07-26  1:36  6%   ` Sam Saffron
@ 2018-07-26  1:37  6%     ` Sam Saffron
  2018-07-26  2:46  5%       ` Eric Wong
  0 siblings, 1 reply; 10+ results
From: Sam Saffron @ 2018-07-26  1:37 UTC (permalink / raw)
  To: Ruby users; +Cc: mwrap-public

Just to clarify here, I mean 2 single global totals, not a per row
kind of thing.

On Thu, Jul 26, 2018 at 11:36 AM, Sam Saffron <sam.saffron@gmail.com> wrote:
> I am using mwrap to debug a little leak at the moment, one feature
> request I do have though is a tally of totals.
>
> It would be nice if it could keep track of total allocated and total
> released. That way if my RSS is bloating I can tell if it is due to
> fragmentation or if it is due to a genuine leak really quick.

^ permalink raw reply	[relevance 6%]

* Re: [ANN] mwrap 2.0.0 mwrap - LD_PRELOAD malloc wrapper for Ruby
  2018-07-20  9:34  6% ` [ANN] " Eric Wong
@ 2018-07-26  1:36  6%   ` Sam Saffron
  2018-07-26  1:37  6%     ` Sam Saffron
  0 siblings, 1 reply; 10+ results
From: Sam Saffron @ 2018-07-26  1:36 UTC (permalink / raw)
  To: Ruby users; +Cc: mwrap-public

I am using mwrap to debug a little leak at the moment, one feature
request I do have though is a tally of totals.

It would be nice if it could keep track of total allocated and total
released. That way if my RSS is bloating I can tell if it is due to
fragmentation or if it is due to a genuine leak really quick.

^ permalink raw reply	[relevance 6%]

* [ANN] mwrap 2.0.0 mwrap - LD_PRELOAD malloc wrapper for Ruby
  2018-07-20  9:25 14% [PATCH] mwrap 2.0.0 mwrap - LD_PRELOAD malloc wrapper for Ruby Eric Wong
@ 2018-07-20  9:34  6% ` Eric Wong
  2018-07-26  1:36  6%   ` Sam Saffron
  0 siblings, 1 reply; 10+ results
From: Eric Wong @ 2018-07-20  9:34 UTC (permalink / raw)
  To: ruby-talk, mwrap-public

Oops, forgot to include links, and title is [ANN] :x

Mailing list:

	https://80x24.org/mwrap-public/
	nntp://80x24.org/inbox.comp.lang.ruby.mwrap
	mailto:mwrap-public@80x24.org (no HTML mail, please)

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

homepage + rdoc: https://80x24.org/mwrap/

^ permalink raw reply	[relevance 6%]

* [PATCH] mwrap 2.0.0 mwrap - LD_PRELOAD malloc wrapper for Ruby
@ 2018-07-20  9:25 14% Eric Wong
  2018-07-20  9:34  6% ` [ANN] " Eric Wong
  0 siblings, 1 reply; 10+ results
From: Eric Wong @ 2018-07-20  9:25 UTC (permalink / raw)
  To: ruby-talk, mwrap-public

mwrap is designed to answer the question:

   Which lines of Ruby are hitting malloc the most?

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:

* GNU/Linux
* FreeBSD (tested 11.1)

It may work on NetBSD, OpenBSD and DragonFly BSD.


Changes in 2.0.0:

This release includes significant changes to track live
allocations and frees.  It can find memory leaks from malloc
with less overhead than valgrind's leakchecker and there is a
new Rack endpoint (MwrapRack) which can display live allocation
stats.

API additions:

* Mwrap#[] - https://80x24.org/mwrap/Mwrap.html#method-c-5B-5D
* Mwrap::SourceLocation - https://80x24.org/mwrap/Mwrap/SourceLocation.html
* MwrapRack - https://80x24.org/mwrap/MwrapRack.html

Incompatible changes:

* Mwrap.clear now an alias to Mwrap.reset; as it's unsafe
  to implement the new Mwrap#[] API otherwise:
  https://80x24.org/mwrap-public/20180716211933.5835-12-e@80x24.org/

26 changes since v1.0.0:

      README: improve usage example
      MANIFEST: add .document
      add benchmark
      use __attribute__((weak)) instead of dlsym
      Mwrap.dump: do not segfault on invalid IO arg
      bin/mwrap: support LISTEN_FDS env from systemd
      support per-allocation headers for per-alloc tracking
      mwrap: use malloc to do our own memalign
      hold RCU read lock to insert each allocation
      realloc: do not copy if allocation failed
      internal_memalign: do not assume real_malloc succeeds
      ensure ENOMEM is preserved in errno when appropriate
      memalign: check alignment on all public functions
      reduce stack usage from file names
      resolve real_malloc earlier for C++ programs
      allow analyzing live allocations via Mwrap[location]
      alias Mwrap.clear to Mwrap.reset
      implement accessors for SourceLocation
      mwrap_aref: quiet -Wshorten-64-to-32 warning
      fixes for FreeBSD 11.1...
      use memrchr to extract address under glibc
      do not track allocations for constructor and Init_
      disable memalign tracking by default
      support Mwrap.quiet to temporarily disable allocation tracking
      mwrap_rack: Rack app to track live allocations
      documentation updates for 2.0.0 release

^ permalink raw reply	[relevance 14%]

* [PATCH] documentation updates for 2.0.0 release
@ 2018-07-20  9:11  7% Eric Wong
  0 siblings, 0 replies; 10+ results
From: Eric Wong @ 2018-07-20  9:11 UTC (permalink / raw)
  To: mwrap-public

---
 .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


^ permalink raw reply related	[relevance 7%]

Results 1-10 of 10 | reverse | options above
-- pct% links below jump to the message on this page, permalinks otherwise --
2018-07-20  9:11  7% [PATCH] documentation updates for 2.0.0 release Eric Wong
2018-07-20  9:25 14% [PATCH] mwrap 2.0.0 mwrap - LD_PRELOAD malloc wrapper for Ruby Eric Wong
2018-07-20  9:34  6% ` [ANN] " Eric Wong
2018-07-26  1:36  6%   ` Sam Saffron
2018-07-26  1:37  6%     ` Sam Saffron
2018-07-26  2:46  5%       ` Eric Wong
2018-07-26  5:02  6%         ` Sam Saffron
2018-07-26  6:21  6%           ` Eric Wong
2018-08-11  4:31  6% [ANN] mwrap 2.1.0 " Eric Wong
2023-01-09  5:52  6% [PATCH] various doc updates, add mwrap(1) manpage Eric Wong

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).