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);