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