ext/stackprof/stackprof.c in stackprof-0.2.10 vs ext/stackprof/stackprof.c in stackprof-0.2.11

- old
+ new

@@ -18,10 +18,11 @@ #define BUF_SIZE 2048 typedef struct { size_t total_samples; size_t caller_samples; + int already_accounted_in_total; st_table *edges; st_table *lines; } frame_data_t; static struct { @@ -36,22 +37,31 @@ VALUE *raw_samples; size_t raw_samples_len; size_t raw_samples_capa; size_t raw_sample_index; + struct timeval last_sample_at; + int *raw_timestamp_deltas; + size_t raw_timestamp_deltas_len; + size_t raw_timestamp_deltas_capa; + size_t overall_signals; size_t overall_samples; size_t during_gc; + size_t unrecorded_gc_samples; st_table *frames; + VALUE fake_gc_frame; + VALUE fake_gc_frame_name; + 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; +static VALUE sym_version, sym_mode, sym_interval, sym_raw, sym_frames, sym_out, sym_aggregate, sym_raw_timestamp_deltas; static VALUE sym_gc_samples, objtracer; static VALUE gc_hook; static VALUE rb_mStackProf; static void stackprof_newobj_handler(VALUE, void*); @@ -118,10 +128,14 @@ _stackprof.aggregate = aggregate; _stackprof.mode = mode; _stackprof.interval = interval; _stackprof.out = out; + if (raw) { + gettimeofday(&_stackprof.last_sample_at, NULL); + } + return Qtrue; } static VALUE stackprof_stop(VALUE self) @@ -185,20 +199,28 @@ VALUE name, file, edges, lines; VALUE line; rb_hash_aset(results, rb_obj_id(frame), details); - name = rb_profile_frame_full_label(frame); - rb_hash_aset(details, sym_name, name); + if (frame == _stackprof.fake_gc_frame) { + name = _stackprof.fake_gc_frame_name; + file = _stackprof.empty_string; + line = INT2FIX(0); + } else { + name = rb_profile_frame_full_label(frame); - file = rb_profile_frame_absolute_path(frame); - if (NIL_P(file)) - file = rb_profile_frame_path(frame); - rb_hash_aset(details, sym_file, file); + file = rb_profile_frame_absolute_path(frame); + if (NIL_P(file)) + file = rb_profile_frame_path(frame); + line = rb_profile_frame_first_lineno(frame); + } - if ((line = rb_profile_frame_first_lineno(frame)) != INT2FIX(0)) + rb_hash_aset(details, sym_name, name); + rb_hash_aset(details, sym_file, file); + if (line != INT2FIX(0)) { rb_hash_aset(details, sym_line, line); + } rb_hash_aset(details, sym_total_samples, SIZET2NUM(frame_data->total_samples)); rb_hash_aset(details, sym_samples, SIZET2NUM(frame_data->caller_samples)); if (frame_data->edges) { @@ -228,11 +250,11 @@ if (!_stackprof.frames || _stackprof.running) return Qnil; results = rb_hash_new(); - rb_hash_aset(results, sym_version, DBL2NUM(1.1)); + rb_hash_aset(results, sym_version, DBL2NUM(1.2)); 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)); @@ -260,13 +282,27 @@ free(_stackprof.raw_samples); _stackprof.raw_samples = NULL; _stackprof.raw_samples_len = 0; _stackprof.raw_samples_capa = 0; _stackprof.raw_sample_index = 0; - _stackprof.raw = 0; rb_hash_aset(results, sym_raw, raw_samples); + + VALUE raw_timestamp_deltas = rb_ary_new_capa(_stackprof.raw_timestamp_deltas_len); + + for (n = 0; n < _stackprof.raw_timestamp_deltas_len; n++) { + rb_ary_push(raw_timestamp_deltas, INT2FIX(_stackprof.raw_timestamp_deltas[n])); + } + + free(_stackprof.raw_timestamp_deltas); + _stackprof.raw_timestamp_deltas = NULL; + _stackprof.raw_timestamp_deltas_len = 0; + _stackprof.raw_timestamp_deltas_capa = 0; + + rb_hash_aset(results, sym_raw_timestamp_deltas, raw_timestamp_deltas); + + _stackprof.raw = 0; } if (argc == 1) _stackprof.out = argv[0]; @@ -338,27 +374,26 @@ { st_update(table, key, numtable_increment_callback, (st_data_t)increment); } void -stackprof_record_sample() +stackprof_record_sample_for_stack(int num, int timestamp_delta) { - int num, i, n; + int i, n; VALUE prev_frame = Qnil; _stackprof.overall_samples++; - num = rb_profile_frames(0, sizeof(_stackprof.frames_buffer) / sizeof(VALUE), _stackprof.frames_buffer, _stackprof.lines_buffer); if (_stackprof.raw) { int found = 0; if (!_stackprof.raw_samples) { _stackprof.raw_samples_capa = num * 100; _stackprof.raw_samples = malloc(sizeof(VALUE) * _stackprof.raw_samples_capa); } - if (_stackprof.raw_samples_capa <= _stackprof.raw_samples_len + num) { + while (_stackprof.raw_samples_capa <= _stackprof.raw_samples_len + (num + 2)) { _stackprof.raw_samples_capa *= 2; _stackprof.raw_samples = realloc(_stackprof.raw_samples, sizeof(VALUE) * _stackprof.raw_samples_capa); } if (_stackprof.raw_samples_len > 0 && _stackprof.raw_samples[_stackprof.raw_sample_index] == (VALUE)num) { @@ -380,18 +415,38 @@ VALUE frame = _stackprof.frames_buffer[i]; _stackprof.raw_samples[_stackprof.raw_samples_len++] = frame; } _stackprof.raw_samples[_stackprof.raw_samples_len++] = (VALUE)1; } + + if (!_stackprof.raw_timestamp_deltas) { + _stackprof.raw_timestamp_deltas_capa = 100; + _stackprof.raw_timestamp_deltas = malloc(sizeof(int) * _stackprof.raw_timestamp_deltas_capa); + _stackprof.raw_timestamp_deltas_len = 0; + } + + while (_stackprof.raw_timestamp_deltas_capa <= _stackprof.raw_timestamp_deltas_len + 1) { + _stackprof.raw_timestamp_deltas_capa *= 2; + _stackprof.raw_timestamp_deltas = realloc(_stackprof.raw_timestamp_deltas, sizeof(int) * _stackprof.raw_timestamp_deltas_capa); + } + + _stackprof.raw_timestamp_deltas[_stackprof.raw_timestamp_deltas_len++] = timestamp_delta; } for (i = 0; i < num; i++) { + VALUE frame = _stackprof.frames_buffer[i]; + sample_for(frame)->already_accounted_in_total = 0; + } + + 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); - frame_data->total_samples++; + if (!frame_data->already_accounted_in_total) + frame_data->total_samples++; + frame_data->already_accounted_in_total = 1; if (i == 0) { frame_data->caller_samples++; } else if (_stackprof.aggregate) { if (!frame_data->edges) @@ -407,13 +462,75 @@ st_numtable_increment(frame_data->lines, (st_data_t)line, increment); } prev_frame = frame; } + + if (_stackprof.raw) { + gettimeofday(&_stackprof.last_sample_at, NULL); + } } +void +stackprof_record_sample() +{ + int timestamp_delta = 0; + if (_stackprof.raw) { + struct timeval t; + gettimeofday(&t, NULL); + struct timeval diff; + timersub(&t, &_stackprof.last_sample_at, &diff); + timestamp_delta = (1000 * diff.tv_sec) + diff.tv_usec; + } + int num = rb_profile_frames(0, sizeof(_stackprof.frames_buffer) / sizeof(VALUE), _stackprof.frames_buffer, _stackprof.lines_buffer); + stackprof_record_sample_for_stack(num, timestamp_delta); +} + +void +stackprof_record_gc_samples() +{ + int delta_to_first_unrecorded_gc_sample = 0; + if (_stackprof.raw) { + struct timeval t; + gettimeofday(&t, NULL); + struct timeval diff; + timersub(&t, &_stackprof.last_sample_at, &diff); + + // We don't know when the GC samples were actually marked, so let's + // assume that they were marked at a perfectly regular interval. + delta_to_first_unrecorded_gc_sample = (1000 * diff.tv_sec + diff.tv_usec) - (_stackprof.unrecorded_gc_samples - 1) * _stackprof.interval; + if (delta_to_first_unrecorded_gc_sample < 0) { + delta_to_first_unrecorded_gc_sample = 0; + } + } + + int i; + + _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 : _stackprof.interval; + stackprof_record_sample_for_stack(1, timestamp_delta); + } + _stackprof.during_gc += _stackprof.unrecorded_gc_samples; + _stackprof.unrecorded_gc_samples = 0; +} + static void +stackprof_gc_job_handler(void *data) +{ + static int in_signal_handler = 0; + if (in_signal_handler) return; + if (!_stackprof.running) return; + + in_signal_handler++; + stackprof_record_gc_samples(); + in_signal_handler--; +} + +static void stackprof_job_handler(void *data) { static int in_signal_handler = 0; if (in_signal_handler) return; if (!_stackprof.running) return; @@ -425,14 +542,16 @@ static void stackprof_signal_handler(int sig, siginfo_t *sinfo, void *ucontext) { _stackprof.overall_signals++; - if (rb_during_gc()) - _stackprof.during_gc++, _stackprof.overall_samples++; - else - rb_postponed_job_register_one(0, stackprof_job_handler, 0); + if (rb_during_gc()) { + _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); + } } static void stackprof_newobj_handler(VALUE tpval, void *data) { @@ -522,16 +641,32 @@ S(lines); S(version); S(mode); S(interval); S(raw); + S(raw_timestamp_deltas); S(out); S(frames); S(aggregate); #undef S 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; + _stackprof.raw_samples_capa = 0; + _stackprof.raw_sample_index = 0; + + _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); 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);