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

  • dump_heap: mask of heap_page_body statistics to dump

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

dump_heap bitmask

  • 0x01 - summary stats (same info as HeapPageBody.stat)

  • 0x02 - all live heaps (similar to HeapPageBody.each)

  • 0x04 - skip non-heap_page_body-related output

Defined Under Namespace

Classes: HeapPageBody, 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]”)



1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
# File 'ext/mwrap/mwrap.c', line 1057

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;

	t = CMM_LOAD_SHARED(totals);
	if (!t) return val;
	rcu_read_lock();

	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);
	}
	rcu_read_unlock();
	return val;
}

.clearObject

:nodoc:



939
940
941
942
# File 'ext/mwrap/mwrap.c', line 939

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



877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
# File 'ext/mwrap/mwrap.c', line 877

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.



1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
# File 'ext/mwrap/mwrap.c', line 1008

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+



1220
1221
1222
1223
1224
# File 'ext/mwrap/mwrap.c', line 1220

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.



932
933
934
935
936
# File 'ext/mwrap/mwrap.c', line 932

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

.total_bytes_allocatedObject

total bytes allocated as tracked by mwrap



1229
1230
1231
1232
# File 'ext/mwrap/mwrap.c', line 1229

static VALUE total_inc(VALUE mod)
{
	return SIZET2NUM(total_bytes_inc);
}

.total_bytes_freedObject

total bytes freed as tracked by mwrap



1237
1238
1239
1240
# File 'ext/mwrap/mwrap.c', line 1237

static VALUE total_dec(VALUE mod)
{
	return SIZET2NUM(total_bytes_dec);
}