Permalink
Browse files

flesh out apis so they can take a filename and write some specs

  • Loading branch information...
1 parent 6bffc26 commit aee83d708d472acce66d689a6b59d3ecc977b885 @tmm1 tmm1 committed Apr 24, 2010
Showing with 177 additions and 48 deletions.
  1. +82 −23 ext/memprof.c
  2. +1 −21 ext/tracer.c
  3. +12 −4 spec/memprof_spec.rb
  4. +82 −0 spec/tracing_spec.rb
View
@@ -360,22 +360,63 @@ memprof_track(int argc, VALUE *argv, VALUE self)
return Qnil;
}
+static yajl_gen_config fancy_conf = { .beautify = 1, .indentString = " " };
+static yajl_gen_config basic_conf = { .beautify = 0, .indentString = " " };
+
+static yajl_gen
+json_for_args(int argc, VALUE *argv)
+{
+ FILE *out = NULL;
+ VALUE str;
+ rb_scan_args(argc, argv, "01", &str);
+
+ if (RTEST(str)) {
+ out = fopen(StringValueCStr(str), "w");
+ if (!out)
+ rb_raise(rb_eArgError, "unable to open output file");
+ }
+
+ if (!out)
+ out = stderr;
+
+ yajl_gen gen = yajl_gen_alloc2((yajl_print_t)&json_print, out == stderr ? &fancy_conf : &basic_conf, NULL, (void*)out);
+
+ return gen;
+}
+
+static void
+json_free(yajl_gen gen)
+{
+ FILE *out = (FILE*)gen->ctx;
+ if (out != stderr)
+ fclose(out);
+ yajl_gen_free(gen);
+}
+
static VALUE
-memprof_trace(VALUE self)
+memprof_trace(int argc, VALUE *argv, VALUE self)
{
if (!rb_block_given_p())
rb_raise(rb_eArgError, "block required");
- trace_set_output(NULL);
- yajl_gen gen = trace_get_output();
+ yajl_gen gen = json_for_args(argc, argv);
+ trace_set_output(gen);
yajl_gen_map_open(gen);
+
trace_invoke_all(TRACE_RESET);
trace_invoke_all(TRACE_START);
+
VALUE ret = rb_yield(Qnil);
+
trace_invoke_all(TRACE_DUMP);
trace_invoke_all(TRACE_STOP);
+
yajl_gen_map_close(gen);
+ yajl_gen_reset(gen);
+
+ json_free(gen);
+ trace_set_output(NULL);
return ret;
}
@@ -396,6 +437,29 @@ each_request_entry(st_data_t key, st_data_t record, st_data_t arg)
return ST_CONTINUE;
}
+static VALUE tracing_json_filename = Qnil;
+static yajl_gen tracing_json_gen = NULL;
+
+static VALUE
+memprof_trace_filename_set(int argc, VALUE *argv, VALUE self)
+{
+ if (tracing_json_gen)
+ json_free(tracing_json_gen);
+
+ if (!RTEST(*argv)) {
+ tracing_json_filename = Qnil;
+ } else {
+ tracing_json_gen = json_for_args(argc, argv);
+ tracing_json_filename = *argv;
+ }
+}
+
+static VALUE
+memprof_trace_filename_get(VALUE self)
+{
+ return tracing_json_filename;
+}
+
static VALUE
memprof_trace_request(VALUE self, VALUE env)
{
@@ -405,8 +469,11 @@ memprof_trace_request(VALUE self, VALUE env)
struct timeval now;
gettimeofday(&now, NULL);
- yajl_gen_config conf = { .beautify = 1, .indentString = " " };
- yajl_gen gen = yajl_gen_alloc2((yajl_print_t)&json_print, &conf, NULL, (void*)stderr);
+ yajl_gen gen;
+ if (tracing_json_gen)
+ gen = tracing_json_gen;
+ else
+ gen = json_for_args(0, NULL);
yajl_gen_map_open(gen);
@@ -434,7 +501,11 @@ memprof_trace_request(VALUE self, VALUE env)
yajl_gen_map_close(gen);
yajl_gen_map_close(gen);
- yajl_gen_free(gen);
+
+ if (gen != tracing_json_gen)
+ json_free(gen);
+ else
+ yajl_gen_reset(gen);
return ret;
}
@@ -1485,32 +1556,18 @@ memprof_dump(int argc, VALUE *argv, VALUE self)
{
VALUE str, ret = Qnil;
int old = track_objs;
- FILE *out = NULL;
if (rb_block_given_p()) {
memprof_start(self);
ret = rb_yield(Qnil);
} else if (!track_objs)
rb_raise(rb_eRuntimeError, "object tracking disabled, call Memprof.start first");
- rb_scan_args(argc, argv, "01", &str);
-
- if (RTEST(str)) {
- out = fopen(StringValueCStr(str), "w");
- if (!out)
- rb_raise(rb_eArgError, "unable to open output file");
- }
-
- yajl_gen_config conf = { .beautify = 0, .indentString = " " };
- yajl_gen gen = yajl_gen_alloc2((yajl_print_t)&json_print, &conf, NULL, (void*)out);
-
track_objs = 0;
+ yajl_gen gen = json_for_args(argc, argv);
st_foreach(objs, objs_each_dump, (st_data_t)gen);
- yajl_gen_free(gen);
-
- if (out)
- fclose(out);
+ json_free(gen);
if (rb_block_given_p())
memprof_stop(self);
@@ -1828,8 +1885,10 @@ Init_memprof()
rb_define_singleton_method(memprof, "track", memprof_track, -1);
rb_define_singleton_method(memprof, "dump", memprof_dump, -1);
rb_define_singleton_method(memprof, "dump_all", memprof_dump_all, -1);
- rb_define_singleton_method(memprof, "trace", memprof_trace, 0);
+ rb_define_singleton_method(memprof, "trace", memprof_trace, -1);
rb_define_singleton_method(memprof, "trace_request", memprof_trace_request, 1);
+ rb_define_singleton_method(memprof, "trace_filename", memprof_trace_filename_get, 0);
+ rb_define_singleton_method(memprof, "trace_filename=", memprof_trace_filename_set, -1);
objs = st_init_numtable();
init_memprof_config_base();
View
@@ -8,8 +8,6 @@
#include "tracer.h"
#include "util.h"
-static yajl_gen_config json_conf = { .beautify = 0, .indentString = " " };
-static yajl_gen default_json = NULL;
static yajl_gen json_gen = NULL;
/*
@@ -107,28 +105,10 @@ trace_invoke(const char *id, trace_fn fn)
return 0;
}
-static void
-json_print(void *ctx, const char * str, unsigned int len)
-{
- FILE *out = (FILE *)ctx;
- size_t written = 0;
- while(1) {
- written += fwrite(str + written, sizeof(char), len - written, out ? out : stdout);
- if (written == len) break;
- }
-}
-
void
trace_set_output(yajl_gen gen)
{
- if (gen) {
- json_gen = gen;
- } else {
- if (!default_json)
- default_json = yajl_gen_alloc2((yajl_print_t)&json_print, &json_conf, NULL, (void*)stderr);
-
- json_gen = default_json;
- }
+ json_gen = gen;
}
yajl_gen
View
@@ -61,17 +61,25 @@ def filedata
1.23+1
Memprof.dump(filename)
- filedata.should =~ /"file": "#{__FILE__}"/
- filedata.should =~ /"line": #{__LINE__-4}/
- filedata.should =~ /"type": "float"/
- filedata.should =~ /"data": 2\.23/
+ filedata.should =~ /"file":"#{__FILE__}"/
+ filedata.should =~ /"line":#{__LINE__-4}/
+ filedata.should =~ /"type":"float"/
+ filedata.should =~ /"data":2\.23/
end
should 'raise error when calling ::stats or ::dump without ::start' do
lambda{ Memprof.stats }.should.raise(RuntimeError).message.should =~ /Memprof.start/
lambda{ Memprof.dump }.should.raise(RuntimeError).message.should =~ /Memprof.start/
end
+ should 'dump objects created for block' do
+ Memprof.dump(filename) do
+ 2.45+1
+ end
+
+ filedata.should =~ /"data":3\.45/
+ end
+
should 'dump out the entire heap' do
Memprof.stop
Memprof.dump_all(filename)
View
@@ -0,0 +1,82 @@
+require File.dirname(__FILE__) + "/../ext/memprof"
+
+require 'rubygems'
+require 'bacon'
+Bacon.summary_on_exit
+
+require 'tempfile'
+
+describe 'Memprof tracers' do
+ @tempfile = Tempfile.new('tracing_spec')
+
+ def filename
+ @tempfile.path
+ end
+
+ def filedata
+ File.read(filename)
+ end
+
+ should 'trace i/o for block of code' do
+ require 'open-uri'
+ Memprof.trace(filename) do
+ open("http://google.com").read
+ end
+
+ filedata.should =~ /"read":\{"calls":\d+/
+ filedata.should =~ /"write":\{"calls":\d+/
+ filedata.should =~ /"connect":\{"calls":\d+/
+ end
+
+ should 'trace objects created for block of code' do
+ Memprof.trace(filename) do
+ 10.times{1.1+1.2}
+ end
+
+ filedata.should =~ /"float":10/
+ end
+
+ should 'trace gc runs for block of code' do
+ Memprof.trace(filename) do
+ 10.times{GC.start}
+ end
+
+ filedata.should =~ /"gc":\{"calls":10,"time":[\d.]+/
+ end
+
+ should 'trace memory allocation for block of code' do
+ Memprof.trace(filename) do
+ 10.times{ "abc" << "def" }
+ end
+
+ filedata.should =~ /"malloc":\{"calls":10/
+ filedata.should =~ /"realloc":\{"calls":10/
+ end
+end
+
+describe 'Memprof request tracing' do
+ @tempfile = Tempfile.new('tracing_spec')
+
+ def filename
+ @tempfile.path
+ end
+
+ def filedata
+ File.read(filename)
+ end
+
+ should 'trace request env' do
+ env = {"HEADER" => "value"}
+
+ Memprof.trace_filename = filename
+ Memprof.trace_filename.should == filename
+
+ Memprof.trace_request(env) do
+ end
+
+ Memprof.trace_filename = nil
+ Memprof.trace_filename.should.be.nil
+
+ filedata.should =~ /"HEADER":"value"/
+ end
+end

0 comments on commit aee83d7

Please sign in to comment.