Module: Mwrap

Defined in:
ext/mwrap/mwrap.c,
ext/mwrap/mwrap.c

Overview

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

  • memalign: use ‘1’ to enable tracking the memalign family

If both ‘dump_fd’ and ‘dump_path’ are specified, dump_path takes precedence.

Tracking the memalign family of functions is misleading for Ruby applications, as heap page allocations can happen anywhere a Ruby object is allocated, even in the coldest code paths. Furthermore, it is rarely-used outside of the Ruby object allocator. Thus tracking memalign functions is disabled by default.

Defined Under Namespace

Classes: SourceLocation

Class Method Summary collapse

Class Method Details

.[](loc) ⇒ Object

Mwrap -> Mwrap::SourceLocation

Returns the associated Mwrap::SourceLocation given the location String. location is either a Ruby source location path:line (e.g. “/path/to/foo.rb:5”) or a hexadecimal memory address with square-braces part yielded by Mwrap.dump (e.g. “[0xdeadbeef]”)



866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
# File 'ext/mwrap/mwrap.c', line 866

static VALUE mwrap_aref(VALUE mod, VALUE loc)
{
	const char *str = StringValueCStr(loc);
	int len = RSTRING_LENINT(loc);
	struct src_loc *k = 0;
	uintptr_t p;
	struct cds_lfht_iter iter;
	struct cds_lfht_node *cur;
	struct cds_lfht *t;
	struct src_loc *l;
	VALUE val = Qnil;

	if (extract_addr(str, len, (void **)&p)) {
		k = (void *)kbuf;
		memcpy(k->k, &p, sizeof(p));
		k->capa = 0;
		k->hval = jhash(k->k, sizeof(p), 0xdeadbeef);
	} else {
		k = (void *)kbuf;
		memcpy(k->k, str, len + 1);
		k->capa = len + 1;
		k->hval = jhash(k->k, k->capa, 0xdeadbeef);
	}

	if (!k) return val;

	rcu_read_lock();
	t = rcu_dereference(totals);
	if (!t) goto out_unlock;

	cds_lfht_lookup(t, k->hval, loc_eq, k, &iter);
	cur = cds_lfht_iter_get_node(&iter);
	if (cur) {
		l = caa_container_of(cur, struct src_loc, hnode);
		val = TypedData_Wrap_Struct(cSrcLoc, &src_loc_type, l);
	}
out_unlock:
	rcu_read_unlock();
	return val;
}

.clearObject

:nodoc:



748
749
750
751
# File 'ext/mwrap/mwrap.c', line 748

static VALUE mwrap_clear(VALUE mod)
{
	return mwrap_reset(mod);
}

.dump(*args) ⇒ Object

Mwrap.dump([ [, 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



690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
# File 'ext/mwrap/mwrap.c', line 690

static VALUE mwrap_dump(int argc, VALUE * argv, VALUE mod)
{
	VALUE io, min;
	struct dump_arg a;
	rb_io_t *fptr;

	rb_scan_args(argc, argv, "02", &io, &min);

	if (NIL_P(io))
		/* library may be linked w/o Ruby */
		io = *((VALUE *)dlsym(RTLD_DEFAULT, "rb_stderr"));

	a.min = NIL_P(min) ? 0 : NUM2SIZET(min);
	io = rb_io_get_io(io);
	io = rb_io_get_write_io(io);
	GetOpenFile(io, fptr);
	a.fp = rb_io_stdio_file(fptr);

	rb_thread_call_without_gvl(dump_to_file, &a, 0, 0);
	RB_GC_GUARD(io);
	return Qnil;
}

.each(*args) ⇒ Object

Mwrap.each() do |location,total,allocations,frees,age_total,max_lifespan|

...

end

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.



817
818
819
820
821
822
823
824
825
826
827
828
829
# File 'ext/mwrap/mwrap.c', line 817

static VALUE mwrap_each(int argc, VALUE * argv, VALUE mod)
{
	VALUE min;
	struct dump_arg a;

	rb_scan_args(argc, argv, "01", &min);
	a.min = NIL_P(min) ? 0 : NUM2SIZET(min);

	++locating;
	rcu_read_lock();

	return rb_ensure(dump_each_rcu, (VALUE)&a, rcu_unlock_ensure, 0);
}

.quietObject

Mwrap.quiet do |depth|

# expensive sort/calculate/emitting results of Mwrap.each
# affecting statistics of the rest of the app

end

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+



1030
1031
1032
1033
1034
# File 'ext/mwrap/mwrap.c', line 1030

static VALUE mwrap_quiet(VALUE mod)
{
	size_t cur = ++locating;
	return rb_ensure(rb_yield, SIZET2NUM(cur), reset_locating, 0);
}

.resetObject

Mwrap.reset -> nil

Resets the the total tables by zero-ing all counters. This resets all statistics. This is not an atomic operation as other threads (outside of GVL) may increment counters.



741
742
743
744
745
# File 'ext/mwrap/mwrap.c', line 741

static VALUE mwrap_reset(VALUE mod)
{
	rb_thread_call_without_gvl(totals_reset, 0, 0, 0);
	return Qnil;
}