ext/stackprof.c in stackprof-0.2.2 vs ext/stackprof.c in stackprof-0.2.3

- old
+ new

@@ -1,22 +1,18 @@ /********************************************************************** stackprof.c - Sampling call-stack frame profiler for MRI. - $Author$ - created at: Thu May 30 17:55:25 2013 + vim: setl noexpandtab shiftwidth=4 tabstop=8 softtabstop=4 - NOTE: This extension library is not expected to exist except C Ruby. - - All the files in this distribution are covered under the Ruby's - license (see the file COPYING). - **********************************************************************/ #include <ruby/ruby.h> #include <ruby/debug.h> #include <ruby/st.h> +#include <ruby/io.h> +#include <ruby/intern.h> #include <signal.h> #include <sys/time.h> #include <pthread.h> #define BUF_SIZE 2048 @@ -30,10 +26,13 @@ static struct { int running; VALUE mode; VALUE interval; + VALUE raw; + size_t raw_sample_index; + VALUE out; size_t overall_signals; size_t overall_samples; size_t during_gc; st_table *frames; @@ -42,11 +41,11 @@ int lines_buffer[BUF_SIZE]; } _stackprof; static VALUE sym_object, sym_wall, sym_cpu, sym_custom, sym_name, sym_file, sym_line; static VALUE sym_samples, sym_total_samples, sym_missed_samples, sym_edges, sym_lines; -static VALUE sym_version, sym_mode, sym_interval, sym_frames; +static VALUE sym_version, sym_mode, sym_interval, sym_raw, sym_frames, sym_out; static VALUE sym_gc_samples, objtracer; static VALUE gc_hook; static VALUE rb_mStackProf; static void stackprof_newobj_handler(VALUE, void*); @@ -55,20 +54,24 @@ static VALUE stackprof_start(int argc, VALUE *argv, VALUE self) { struct sigaction sa; struct itimerval timer; - VALUE opts = Qnil, mode = Qnil, interval = Qnil; + VALUE opts = Qnil, mode = Qnil, interval = Qnil, raw = Qfalse, out = Qfalse; if (_stackprof.running) return Qfalse; rb_scan_args(argc, argv, "0:", &opts); if (RTEST(opts)) { mode = rb_hash_aref(opts, sym_mode); interval = rb_hash_aref(opts, sym_interval); + out = rb_hash_aref(opts, sym_out); + + if (RTEST(rb_hash_aref(opts, sym_raw))) + raw = rb_ary_new(); } if (!RTEST(mode)) mode = sym_wall; if (!_stackprof.frames) { _stackprof.frames = st_init_numtable(); @@ -100,12 +103,14 @@ } else { rb_raise(rb_eArgError, "unknown profiler mode"); } _stackprof.running = 1; + _stackprof.raw = raw; _stackprof.mode = mode; _stackprof.interval = interval; + _stackprof.out = out; return Qtrue; } static VALUE @@ -205,11 +210,11 @@ xfree(frame_data); return ST_DELETE; } static VALUE -stackprof_results(VALUE self) +stackprof_results(int argc, VALUE *argv, VALUE self) { VALUE results, frames; if (!_stackprof.frames || _stackprof.running) return Qnil; @@ -227,20 +232,41 @@ st_foreach(_stackprof.frames, frame_i, (st_data_t)frames); st_free_table(_stackprof.frames); _stackprof.frames = NULL; - return results; + if (RTEST(_stackprof.raw)) { + rb_hash_aset(results, sym_raw, _stackprof.raw); + _stackprof.raw = Qfalse; + } + + if (argc == 1) + _stackprof.out = argv[0]; + + if (RTEST(_stackprof.out)) { + VALUE file; + if (RB_TYPE_P(_stackprof.out, T_STRING)) { + file = rb_file_open_str(_stackprof.out, "w"); + } else { + file = rb_io_check_io(_stackprof.out); + } + rb_marshal_dump(results, file); + rb_io_flush(file); + _stackprof.out = Qnil; + return file; + } else { + return results; + } } static VALUE stackprof_run(int argc, VALUE *argv, VALUE self) { rb_need_block(); stackprof_start(argc, argv, self); rb_ensure(rb_yield, Qundef, stackprof_stop, self); - return stackprof_results(self); + return stackprof_results(0, 0, self); } static VALUE stackprof_running_p(VALUE self) { @@ -286,16 +312,45 @@ } void stackprof_record_sample() { - int num, i; + int num, i, n; + int raw_mode = RTEST(_stackprof.raw); VALUE prev_frame = Qnil; + size_t raw_len; _stackprof.overall_samples++; num = rb_profile_frames(0, sizeof(_stackprof.frames_buffer), _stackprof.frames_buffer, _stackprof.lines_buffer); + if (raw_mode) { + int found = 0; + raw_len = RARRAY_LEN(_stackprof.raw); + + if (RARRAY_LEN(_stackprof.raw) > 0 && RARRAY_AREF(_stackprof.raw, _stackprof.raw_sample_index) == INT2FIX(num)) { + for (i = num-1, n = 0; i >= 0; i--, n++) { + VALUE frame = _stackprof.frames_buffer[i]; + if (RARRAY_AREF(_stackprof.raw, _stackprof.raw_sample_index + 1 + n) != rb_obj_id(frame)) + break; + } + if (i == -1) { + RARRAY_ASET(_stackprof.raw, raw_len-1, LONG2NUM(NUM2LONG(RARRAY_AREF(_stackprof.raw, raw_len-1))+1)); + found = 1; + } + } + + if (!found) { + _stackprof.raw_sample_index = raw_len; + rb_ary_push(_stackprof.raw, INT2FIX(num)); + for (i = num-1; i >= 0; i--) { + VALUE frame = _stackprof.frames_buffer[i]; + rb_ary_push(_stackprof.raw, rb_obj_id(frame)); + } + rb_ary_push(_stackprof.raw, INT2FIX(1)); + } + } + for (i = 0; i < num; i++) { int line = _stackprof.lines_buffer[i]; VALUE frame = _stackprof.frames_buffer[i]; frame_data_t *frame_data = sample_for(frame); @@ -344,10 +399,11 @@ } static void stackprof_newobj_handler(VALUE tpval, void *data) { + /* TODO: implement interval */ _stackprof.overall_signals++; stackprof_job_handler(0); } static VALUE @@ -370,10 +426,15 @@ } static void stackprof_gc_mark(void *data) { + if (RTEST(_stackprof.raw)) + rb_gc_mark(_stackprof.raw); + if (RTEST(_stackprof.out)) + rb_gc_mark(_stackprof.out); + if (_stackprof.frames) st_foreach(_stackprof.frames, frame_mark_i, 0); } static void @@ -425,20 +486,22 @@ sym_edges = ID2SYM(rb_intern("edges")); sym_lines = ID2SYM(rb_intern("lines")); sym_version = ID2SYM(rb_intern("version")); sym_mode = ID2SYM(rb_intern("mode")); sym_interval = ID2SYM(rb_intern("interval")); + sym_raw = ID2SYM(rb_intern("raw")); + sym_out = ID2SYM(rb_intern("out")); sym_frames = ID2SYM(rb_intern("frames")); gc_hook = Data_Wrap_Struct(rb_cObject, stackprof_gc_mark, NULL, NULL); rb_global_variable(&gc_hook); rb_mStackProf = rb_define_module("StackProf"); rb_define_singleton_method(rb_mStackProf, "running?", stackprof_running_p, 0); rb_define_singleton_method(rb_mStackProf, "run", stackprof_run, -1); rb_define_singleton_method(rb_mStackProf, "start", stackprof_start, -1); rb_define_singleton_method(rb_mStackProf, "stop", stackprof_stop, 0); - rb_define_singleton_method(rb_mStackProf, "results", stackprof_results, 0); + rb_define_singleton_method(rb_mStackProf, "results", stackprof_results, -1); rb_define_singleton_method(rb_mStackProf, "sample", stackprof_sample, 0); rb_autoload(rb_mStackProf, rb_intern_const("Report"), "stackprof/report.rb"); rb_autoload(rb_mStackProf, rb_intern_const("Middleware"), "stackprof/middleware.rb");