ext/stackprof/stackprof.c in stackprof-0.2.13 vs ext/stackprof/stackprof.c in stackprof-0.2.14

- old
+ new

@@ -15,10 +15,22 @@ #include <sys/time.h> #include <pthread.h> #define BUF_SIZE 2048 +#define FAKE_FRAME_GC INT2FIX(0) +#define FAKE_FRAME_MARK INT2FIX(1) +#define FAKE_FRAME_SWEEP INT2FIX(2) + +static const char *fake_frame_cstrs[] = { + "(garbage collection)", + "(marking)", + "(sweeping)", +}; + +#define TOTAL_FAKE_FRAMES (sizeof(fake_frame_cstrs) / sizeof(char *)) + typedef struct { size_t total_samples; size_t caller_samples; size_t seen_at_sample_number; st_table *edges; @@ -31,10 +43,11 @@ int aggregate; VALUE mode; VALUE interval; VALUE out; + VALUE metadata; VALUE *raw_samples; size_t raw_samples_len; size_t raw_samples_capa; size_t raw_sample_index; @@ -46,22 +59,24 @@ size_t overall_signals; size_t overall_samples; size_t during_gc; size_t unrecorded_gc_samples; + size_t unrecorded_gc_marking_samples; + size_t unrecorded_gc_sweeping_samples; st_table *frames; - VALUE fake_gc_frame; - VALUE fake_gc_frame_name; + VALUE fake_frame_names[TOTAL_FAKE_FRAMES]; VALUE empty_string; VALUE frames_buffer[BUF_SIZE]; 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_raw, sym_frames, sym_out, sym_aggregate, sym_raw_timestamp_deltas; +static VALUE sym_version, sym_mode, sym_interval, sym_raw, sym_metadata, sym_frames, sym_out, sym_aggregate, sym_raw_timestamp_deltas; +static VALUE sym_state, sym_marking, sym_sweeping; static VALUE sym_gc_samples, objtracer; static VALUE gc_hook; static VALUE rb_mStackProf; static void stackprof_newobj_handler(VALUE, void*); @@ -70,11 +85,11 @@ static VALUE stackprof_start(int argc, VALUE *argv, VALUE self) { struct sigaction sa; struct itimerval timer; - VALUE opts = Qnil, mode = Qnil, interval = Qnil, out = Qfalse; + VALUE opts = Qnil, mode = Qnil, interval = Qnil, metadata = rb_hash_new(), out = Qfalse; int raw = 0, aggregate = 1; if (_stackprof.running) return Qfalse; @@ -83,10 +98,18 @@ if (RTEST(opts)) { mode = rb_hash_aref(opts, sym_mode); interval = rb_hash_aref(opts, sym_interval); out = rb_hash_aref(opts, sym_out); + VALUE metadata_val = rb_hash_aref(opts, sym_metadata); + if (RTEST(metadata_val)) { + if (!RB_TYPE_P(metadata_val, T_HASH)) + rb_raise(rb_eArgError, "metadata should be a hash"); + + metadata = metadata_val; + } + if (RTEST(rb_hash_aref(opts, sym_raw))) raw = 1; if (rb_hash_lookup2(opts, sym_aggregate, Qundef) == Qfalse) aggregate = 0; } @@ -126,10 +149,11 @@ _stackprof.running = 1; _stackprof.raw = raw; _stackprof.aggregate = aggregate; _stackprof.mode = mode; _stackprof.interval = interval; + _stackprof.metadata = metadata; _stackprof.out = out; if (raw) { gettimeofday(&_stackprof.last_sample_at, NULL); } @@ -199,12 +223,12 @@ VALUE name, file, edges, lines; VALUE line; rb_hash_aset(results, rb_obj_id(frame), details); - if (frame == _stackprof.fake_gc_frame) { - name = _stackprof.fake_gc_frame_name; + if (FIXNUM_P(frame)) { + name = _stackprof.fake_frame_names[FIX2INT(frame)]; file = _stackprof.empty_string; line = INT2FIX(0); } else { name = rb_profile_frame_full_label(frame); @@ -256,10 +280,11 @@ rb_hash_aset(results, sym_mode, _stackprof.mode); rb_hash_aset(results, sym_interval, _stackprof.interval); rb_hash_aset(results, sym_samples, SIZET2NUM(_stackprof.overall_samples)); rb_hash_aset(results, sym_gc_samples, SIZET2NUM(_stackprof.during_gc)); rb_hash_aset(results, sym_missed_samples, SIZET2NUM(_stackprof.overall_signals - _stackprof.overall_samples)); + rb_hash_aset(results, sym_metadata, _stackprof.metadata); frames = rb_hash_new(); rb_hash_aset(results, sym_frames, frames); st_foreach(_stackprof.frames, frame_i, (st_data_t)frames); @@ -520,19 +545,41 @@ if (delta_to_first_unrecorded_gc_sample < 0) { delta_to_first_unrecorded_gc_sample = 0; } } - _stackprof.frames_buffer[0] = _stackprof.fake_gc_frame; - _stackprof.lines_buffer[0] = 0; for (i = 0; i < _stackprof.unrecorded_gc_samples; i++) { int timestamp_delta = i == 0 ? delta_to_first_unrecorded_gc_sample : NUM2LONG(_stackprof.interval); - stackprof_record_sample_for_stack(1, timestamp_delta); + + if (_stackprof.unrecorded_gc_marking_samples) { + _stackprof.frames_buffer[0] = FAKE_FRAME_MARK; + _stackprof.lines_buffer[0] = 0; + _stackprof.frames_buffer[1] = FAKE_FRAME_GC; + _stackprof.lines_buffer[1] = 0; + _stackprof.unrecorded_gc_marking_samples--; + + stackprof_record_sample_for_stack(2, timestamp_delta); + } else if (_stackprof.unrecorded_gc_sweeping_samples) { + _stackprof.frames_buffer[0] = FAKE_FRAME_SWEEP; + _stackprof.lines_buffer[0] = 0; + _stackprof.frames_buffer[1] = FAKE_FRAME_GC; + _stackprof.lines_buffer[1] = 0; + + _stackprof.unrecorded_gc_sweeping_samples--; + + stackprof_record_sample_for_stack(2, timestamp_delta); + } else { + _stackprof.frames_buffer[0] = FAKE_FRAME_GC; + _stackprof.lines_buffer[0] = 0; + stackprof_record_sample_for_stack(1, timestamp_delta); + } } _stackprof.during_gc += _stackprof.unrecorded_gc_samples; _stackprof.unrecorded_gc_samples = 0; + _stackprof.unrecorded_gc_marking_samples = 0; + _stackprof.unrecorded_gc_sweeping_samples = 0; } static void stackprof_gc_job_handler(void *data) { @@ -560,10 +607,16 @@ static void stackprof_signal_handler(int sig, siginfo_t *sinfo, void *ucontext) { _stackprof.overall_signals++; if (rb_during_gc()) { + VALUE mode = rb_gc_latest_gc_info(sym_state); + if (mode == sym_marking) { + _stackprof.unrecorded_gc_marking_samples++; + } else if (mode == sym_sweeping) { + _stackprof.unrecorded_gc_sweeping_samples++; + } _stackprof.unrecorded_gc_samples++; rb_postponed_job_register_one(0, stackprof_gc_job_handler, (void*)0); } else { rb_postponed_job_register_one(0, stackprof_job_handler, (void*)0); } @@ -640,10 +693,11 @@ } void Init_stackprof(void) { + size_t i; #define S(name) sym_##name = ID2SYM(rb_intern(#name)); S(object); S(custom); S(wall); S(cpu); @@ -660,14 +714,21 @@ S(mode); S(interval); S(raw); S(raw_timestamp_deltas); S(out); + S(metadata); S(frames); S(aggregate); + S(state); + S(marking); + S(sweeping); #undef S + /* Need to run this to warm the symbol table before we call this during GC */ + rb_gc_latest_gc_info(sym_state); + gc_hook = Data_Wrap_Struct(rb_cObject, stackprof_gc_mark, NULL, &_stackprof); rb_global_variable(&gc_hook); _stackprof.raw_samples = NULL; _stackprof.raw_samples_len = 0; @@ -676,14 +737,16 @@ _stackprof.raw_timestamp_deltas = NULL; _stackprof.raw_timestamp_deltas_len = 0; _stackprof.raw_timestamp_deltas_capa = 0; - _stackprof.fake_gc_frame = INT2FIX(0x9C); _stackprof.empty_string = rb_str_new_cstr(""); - _stackprof.fake_gc_frame_name = rb_str_new_cstr("(garbage collection)"); - rb_global_variable(&_stackprof.fake_gc_frame_name); rb_global_variable(&_stackprof.empty_string); + + for (i = 0; i < TOTAL_FAKE_FRAMES; i++) { + _stackprof.fake_frame_names[i] = rb_str_new_cstr(fake_frame_cstrs[i]); + rb_global_variable(&_stackprof.fake_frame_names[i]); + } 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);