module StackProf

Public Class Methods

results(*args) click to toggle source
static VALUE
stackprof_results(int argc, VALUE *argv, VALUE self)
{
    VALUE results, frames;

    if (!_stackprof.frames || _stackprof.running)
        return Qnil;

    results = rb_hash_new();
    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));

    frames = rb_hash_new();
    rb_hash_aset(results, sym_frames, frames);
    st_foreach(_stackprof.frames, frame_i, (st_data_t)frames);

    st_free_table(_stackprof.frames);
    _stackprof.frames = NULL;

    if (_stackprof.raw && _stackprof.raw_samples_len) {
        size_t len, n, o;
        VALUE raw_timestamp_deltas;
        VALUE raw_samples = rb_ary_new_capa(_stackprof.raw_samples_len);

        for (n = 0; n < _stackprof.raw_samples_len; n++) {
            len = (size_t)_stackprof.raw_samples[n];
            rb_ary_push(raw_samples, SIZET2NUM(len));

            for (o = 0, n++; o < len; n++, o++)
                rb_ary_push(raw_samples, rb_obj_id(_stackprof.raw_samples[n]));
            rb_ary_push(raw_samples, SIZET2NUM((size_t)_stackprof.raw_samples[n]));
        }

        free(_stackprof.raw_samples);
        _stackprof.raw_samples = NULL;
        _stackprof.raw_samples_len = 0;
        _stackprof.raw_samples_capa = 0;
        _stackprof.raw_sample_index = 0;

        rb_hash_aset(results, sym_raw, raw_samples);

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

    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;
    }
}
run(*args) click to toggle source
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(0, 0, self);
}
running?() click to toggle source
static VALUE
stackprof_running_p(VALUE self)
{
    return _stackprof.running ? Qtrue : Qfalse;
}
sample() click to toggle source
static VALUE
stackprof_sample(VALUE self)
{
    if (!_stackprof.running)
        return Qfalse;

    _stackprof.overall_signals++;
    stackprof_job_handler(0);
    return Qtrue;
}
start(p1 = {}) click to toggle source
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;
    int raw = 0, aggregate = 1;

    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 = 1;
        if (rb_hash_lookup2(opts, sym_aggregate, Qundef) == Qfalse)
            aggregate = 0;
    }
    if (!RTEST(mode)) mode = sym_wall;

    if (!_stackprof.frames) {
        _stackprof.frames = st_init_numtable();
        _stackprof.overall_signals = 0;
        _stackprof.overall_samples = 0;
        _stackprof.during_gc = 0;
    }

    if (mode == sym_object) {
        if (!RTEST(interval)) interval = INT2FIX(1);

        objtracer = rb_tracepoint_new(Qnil, RUBY_INTERNAL_EVENT_NEWOBJ, stackprof_newobj_handler, 0);
        rb_tracepoint_enable(objtracer);
    } else if (mode == sym_wall || mode == sym_cpu) {
        if (!RTEST(interval)) interval = INT2FIX(1000);

        sa.sa_sigaction = stackprof_signal_handler;
        sa.sa_flags = SA_RESTART | SA_SIGINFO;
        sigemptyset(&sa.sa_mask);
        sigaction(mode == sym_wall ? SIGALRM : SIGPROF, &sa, NULL);

        timer.it_interval.tv_sec = 0;
        timer.it_interval.tv_usec = NUM2LONG(interval);
        timer.it_value = timer.it_interval;
        setitimer(mode == sym_wall ? ITIMER_REAL : ITIMER_PROF, &timer, 0);
    } else if (mode == sym_custom) {
        /* sampled manually */
        interval = Qnil;
    } else {
        rb_raise(rb_eArgError, "unknown profiler mode");
    }

    _stackprof.running = 1;
    _stackprof.raw = raw;
    _stackprof.aggregate = aggregate;
    _stackprof.mode = mode;
    _stackprof.interval = interval;
    _stackprof.out = out;

    if (raw) {
        gettimeofday(&_stackprof.last_sample_at, NULL);
    }

    return Qtrue;
}
stop() click to toggle source
static VALUE
stackprof_stop(VALUE self)
{
    struct sigaction sa;
    struct itimerval timer;

    if (!_stackprof.running)
        return Qfalse;
    _stackprof.running = 0;

    if (_stackprof.mode == sym_object) {
        rb_tracepoint_disable(objtracer);
    } else if (_stackprof.mode == sym_wall || _stackprof.mode == sym_cpu) {
        memset(&timer, 0, sizeof(timer));
        setitimer(_stackprof.mode == sym_wall ? ITIMER_REAL : ITIMER_PROF, &timer, 0);

        sa.sa_handler = SIG_IGN;
        sa.sa_flags = SA_RESTART;
        sigemptyset(&sa.sa_mask);
        sigaction(_stackprof.mode == sym_wall ? SIGALRM : SIGPROF, &sa, NULL);
    } else if (_stackprof.mode == sym_custom) {
        /* sampled manually */
    } else {
        rb_raise(rb_eArgError, "unknown profiler mode");
    }

    return Qtrue;
}