Skip to content

Add option to specify stack frame depth of CPU sampler #129

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,8 @@ Rbkit.start_profiling(
enable_cpu_profiling: true,
clock_type: :wall,
cpu_profiling_mode: :sampling,
cpu_sampling_interval_usec: 1000
cpu_sampling_interval_usec: 1000,
cpu_sampling_depth: 10
)
```

Expand All @@ -122,6 +123,7 @@ Arguments:
|clock_type | :wall/:cpu | :wall | Specifies clock type to use in CPU profiling |
|cpu_profiling_mode | :sampling | :sampling | CPU profiling mode - currently only sampling |
|cpu_sampling_interval_usec | Fixnums | 1000 | CPU Sampling interval un usec, if sampling mode |
|cpu_sampling_depth | Fixnums | 10 | Depth of stack frame collected by CPU sampler |


## Development
Expand Down
8 changes: 6 additions & 2 deletions ext/rbkit_sampling_profiler.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

static int signal_type;
static int clock_type;
static int sampling_depth;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we move all these sampling profiling related variables into a struct?

queue_sample_func_ptr queue_cpu_sample_for_sending;

#ifdef RBKIT_DEV
Expand All @@ -30,7 +31,9 @@ static void sampling_job_handler(void *data_unused) {

rbkit_cpu_sample *sample = malloc(sizeof(rbkit_cpu_sample));

int collected_size = rb_profile_frames(start, sizeof(buff) / sizeof(VALUE), buff, lines);
int max_depth = sizeof(buff) / sizeof(VALUE);
int depth = (sampling_depth > max_depth) ? max_depth : sampling_depth;
int collected_size = rb_profile_frames(start, depth, buff, lines);
rbkit_frame_data *frame_data = malloc(sizeof(rbkit_frame_data) * collected_size);
sample->frames = frame_data;
sample->frame_count = collected_size;
Expand Down Expand Up @@ -118,8 +121,9 @@ static void stop_sigprof_timer() {
setitimer(clock_type, &timer, 0);
}

void rbkit_install_sampling_profiler(int wall_time, int interval, queue_sample_func_ptr func) {
void rbkit_install_sampling_profiler(int wall_time, int interval, int depth, queue_sample_func_ptr func) {
queue_cpu_sample_for_sending = func;
sampling_depth = depth;
if(wall_time) {
signal_type = SIGALRM;
clock_type = ITIMER_REAL;
Expand Down
2 changes: 1 addition & 1 deletion ext/rbkit_sampling_profiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@ typedef struct _rbkit_cpu_sample {

typedef void (*queue_sample_func_ptr) (rbkit_cpu_sample *);

void rbkit_install_sampling_profiler(int wall_time, int interval, queue_sample_func_ptr func);
void rbkit_install_sampling_profiler(int wall_time, int interval, int depth, queue_sample_func_ptr func);
void rbkit_uninstall_sampling_profiler();
#endif
6 changes: 3 additions & 3 deletions ext/rbkit_server.c
Original file line number Diff line number Diff line change
Expand Up @@ -295,12 +295,12 @@ static void queue_cpu_sample_for_sending(rbkit_cpu_sample *sample) {
msgpack_packer_free(packer);
}

static VALUE start_sampling_profiler(VALUE self, VALUE clock_type, VALUE interval) {
static VALUE start_sampling_profiler(VALUE self, VALUE clock_type, VALUE interval, VALUE depth) {
if (logger->sampling_profiler_enabled == Qtrue)
return Qnil;
if(!SYMBOL_P(clock_type))
rb_raise(rb_eArgError, "clock_type should be a symbol, either :wall or :cpu");
rbkit_install_sampling_profiler(clock_type == ID2SYM(rb_intern("wall")), FIX2INT(interval), queue_cpu_sample_for_sending);
rbkit_install_sampling_profiler(clock_type == ID2SYM(rb_intern("wall")), FIX2INT(interval), FIX2INT(depth), queue_cpu_sample_for_sending);
logger->sampling_profiler_enabled = Qtrue;
return Qnil;
}
Expand Down Expand Up @@ -424,7 +424,7 @@ void Init_rbkit_server(void) {
rb_define_method(rbkit_server, "stop_stat_server", stop_stat_server, 0);
rb_define_method(rbkit_server, "start_stat_tracing", start_stat_tracing, 0);
rb_define_method(rbkit_server, "stop_stat_tracing", stop_stat_tracing, 0);
rb_define_method(rbkit_server, "start_sampling_profiler", start_sampling_profiler, 2);
rb_define_method(rbkit_server, "start_sampling_profiler", start_sampling_profiler, 3);
rb_define_method(rbkit_server, "stop_sampling_profiler", stop_sampling_profiler, 0);
rb_define_method(rbkit_server, "poll_for_request", poll_for_request, 0);
rb_define_method(rbkit_server, "send_objectspace_dump", send_objectspace_dump, 0);
Expand Down
4 changes: 2 additions & 2 deletions lib/rbkit.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@ def self.start_server(pub_port: DEFAULT_PUB_PORT, request_port: DEFAULT_REQ_PORT
def self.start_profiling(pub_port: DEFAULT_PUB_PORT, request_port: DEFAULT_REQ_PORT,
enable_object_trace: true, enable_gc_stats: true,
enable_cpu_profiling: true, clock_type: :wall,
cpu_profiling_mode: :sampling, cpu_sampling_interval_usec: 1000)
cpu_profiling_mode: :sampling, cpu_sampling_interval_usec: 1000, cpu_sampling_depth: 10)
@server ||= Rbkit::Server.new(pub_port, request_port)
@server.start(enable_object_trace: enable_object_trace,
enable_gc_stats: enable_gc_stats,
enable_cpu_profiling: enable_cpu_profiling,
clock_type: clock_type,
cpu_sampling_interval_usec: cpu_sampling_interval_usec)
cpu_sampling_interval_usec: cpu_sampling_interval_usec, cpu_sampling_depth: cpu_sampling_depth)
end

# Stops profiling and brings down the rbkit server if it's running
Expand Down
12 changes: 7 additions & 5 deletions lib/rbkit/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ def initialize(pub_port, request_port)
@clock_type = :cpu
@cpu_profiling_mode = :sampling
@cpu_sampling_interval_usec = 1000
@cpu_sampling_depth = 10
@gc_stats_timer = Rbkit::Timer.new(5) do
data = RbkitGC.stat
send_hash_as_event(data, Rbkit::EVENT_TYPES[:gc_stats])
Expand All @@ -22,7 +23,7 @@ def initialize(pub_port, request_port)
end

def start(enable_object_trace: false, enable_gc_stats: false,
enable_cpu_profiling: false, clock_type: nil, cpu_profiling_mode: nil, cpu_sampling_interval_usec: nil)
enable_cpu_profiling: false, clock_type: nil, cpu_profiling_mode: nil, cpu_sampling_interval_usec: nil, cpu_sampling_depth: nil)
if @server_running || !start_stat_server(pub_port, request_port)
$stderr.puts "Rbkit server couldn't bind to socket, check if it is already" \
" running. Profiling data will not be available."
Expand All @@ -32,7 +33,8 @@ def start(enable_object_trace: false, enable_gc_stats: false,
@cpu_profiling_mode = cpu_profiling_mode if cpu_profiling_mode
@clock_type = clock_type if clock_type
@cpu_sampling_interval_usec = cpu_sampling_interval_usec if cpu_sampling_interval_usec
start_cpu_profiling(clock_type: @clock_type, sampling_interval_usec: @cpu_sampling_interval_usec) if enable_cpu_profiling
@cpu_sampling_depth = cpu_sampling_depth if cpu_sampling_depth
start_cpu_profiling(clock_type: @clock_type, sampling_interval_usec: @cpu_sampling_interval_usec, sampling_depth: @cpu_sampling_depth) if enable_cpu_profiling
@enable_gc_stats = enable_gc_stats
@server_running = true
@profiler_thread = Thread.new do
Expand Down Expand Up @@ -67,7 +69,7 @@ def process_incoming_request(incoming_request)
when "use_wall_time"
@clock_type = :wall
when "start_cpu_profiling"
start_cpu_profiling(clock_type: @clock_type, sampling_interval_usec: @cpu_sampling_interval_usec)
start_cpu_profiling(clock_type: @clock_type, sampling_interval_usec: @cpu_sampling_interval_usec, sampling_depth: @cpu_sampling_depth)
when "stop_cpu_profiling"
stop_cpu_profiling
end
Expand All @@ -82,10 +84,10 @@ def stop
true
end

def start_cpu_profiling(mode: :sampling, clock_type: :wall, sampling_interval_usec: 1000)
def start_cpu_profiling(mode: :sampling, clock_type: :wall, sampling_interval_usec: 1000, sampling_depth: 10)
@cpu_profiling_mode = mode
if mode == :sampling
start_sampling_profiler(clock_type, sampling_interval_usec)
start_sampling_profiler(clock_type, sampling_interval_usec, sampling_depth)
else
# TODO
end
Expand Down
121 changes: 62 additions & 59 deletions spec/cpu_sampling_spec.rb
Original file line number Diff line number Diff line change
@@ -1,76 +1,51 @@
require 'spec_helper'
require 'support/cpu_profiling_example'
require 'support/cpu_sample_helpers'
include CpuSampleHelpers

describe 'CPU Sampling' do
let(:sampling_interval_in_usec) { 1000 }

def io_intensive_operation
r, w = IO.pipe
IO.select([r], nil, nil, 1)
ensure
r.close
w.close
end

def cpu_intensive_operation
100000.times{|x| Math.sqrt(x)}
end

class SampleClassForTest
class Sample2
def baz(block)
instance_eval 'def zab(block) block.call end'
[self, zab(block)]
end
end

def self.bar(block)
Sample2.new.baz(block)
end

def foo(block)
self.class.bar(block)
end
end

before do
server = Rbkit.start_profiling(enable_gc_stats: false, enable_object_trace: false,
enable_cpu_profiling: true, clock_type: clock_type,
cpu_profiling_mode: :sampling, cpu_sampling_interval_usec: sampling_interval_in_usec)
cpu_profiling_mode: :sampling, cpu_sampling_interval_usec: sampling_interval_in_usec,
cpu_sampling_depth: stack_depth)
Fiber.new {
Fiber.yield SampleClassForTest.new.foo(operation)
}.resume
@messages = MessagePack.unpack server.get_queued_messages
server.stop
end

let(:sampling_interval_in_usec) { 1000 }
let(:stack_depth) { 10 }
let(:file) { File.absolute_path(File.join(__FILE__, '../support/cpu_profiling_example.rb')) }

context 'when using wall time' do
let(:clock_type) { :wall }
let(:operation) { lambda{ io_intensive_operation; cpu_intensive_operation; } }

it 'should record the correct stack frames for io intensive operation' do
# Make sure sampling can detect most expensive method
expect(@messages).to have_message(Rbkit::EVENT_TYPES[:cpu_sample])
expect(@messages).to have_most_cpu_samples_for("#{__FILE__}:8")
expect(@messages).to have_most_cpu_samples_for("#{file}:1")

# Make sure stack trace is correct
frames = most_frequent_sample(@messages)
expect(frames.size).to eql 7

frame = frames[0]
expect(frame[Rbkit::MESSAGE_FIELDS[:method_name]]).to eql 'io_intensive_operation'
expect(frame[Rbkit::MESSAGE_FIELDS[:label]]).to eql 'RSpec::ExampleGroups::CPUSampling#io_intensive_operation'
expect(frame[Rbkit::MESSAGE_FIELDS[:file]]).to eql __FILE__
expect(frame[Rbkit::MESSAGE_FIELDS[:line]]).to eql 8
expect(frame[Rbkit::MESSAGE_FIELDS[:label]]).to eql 'Object#io_intensive_operation'
expect(frame[Rbkit::MESSAGE_FIELDS[:file]]).to eql file
expect(frame[Rbkit::MESSAGE_FIELDS[:line]]).to eql 1
expect(frame[Rbkit::MESSAGE_FIELDS[:singleton_method]]).to eql 0
expect(frame[Rbkit::MESSAGE_FIELDS[:thread_id]]).to eql Thread.current.object_id

frame = frames[1]
expect(frame[Rbkit::MESSAGE_FIELDS[:method_name]]).to be_nil
expect(frame[Rbkit::MESSAGE_FIELDS[:label]]).to eql 'block (4 levels) in <top (required)>'
expect(frame[Rbkit::MESSAGE_FIELDS[:file]]).to eql __FILE__
expect(frame[Rbkit::MESSAGE_FIELDS[:line]]).to eql 50
expect(frame[Rbkit::MESSAGE_FIELDS[:line]]).to eql 25
expect(frame[Rbkit::MESSAGE_FIELDS[:singleton_method]]).to eql 0
expect(frame[Rbkit::MESSAGE_FIELDS[:thread_id]]).to eql Thread.current.object_id

Expand All @@ -85,32 +60,32 @@ def foo(block)
frame = frames[3]
expect(frame[Rbkit::MESSAGE_FIELDS[:method_name]]).to eql 'baz'
expect(frame[Rbkit::MESSAGE_FIELDS[:label]]).to eql 'SampleClassForTest::Sample2#baz'
expect(frame[Rbkit::MESSAGE_FIELDS[:file]]).to eql __FILE__
expect(frame[Rbkit::MESSAGE_FIELDS[:line]]).to eql 22
expect(frame[Rbkit::MESSAGE_FIELDS[:file]]).to eql file
expect(frame[Rbkit::MESSAGE_FIELDS[:line]]).to eql 15
expect(frame[Rbkit::MESSAGE_FIELDS[:singleton_method]]).to eql 0
expect(frame[Rbkit::MESSAGE_FIELDS[:thread_id]]).to eql Thread.current.object_id

frame = frames[4]
expect(frame[Rbkit::MESSAGE_FIELDS[:method_name]]).to eql 'bar'
expect(frame[Rbkit::MESSAGE_FIELDS[:label]]).to eql 'SampleClassForTest.bar'
expect(frame[Rbkit::MESSAGE_FIELDS[:file]]).to eql __FILE__
expect(frame[Rbkit::MESSAGE_FIELDS[:line]]).to eql 28
expect(frame[Rbkit::MESSAGE_FIELDS[:file]]).to eql file
expect(frame[Rbkit::MESSAGE_FIELDS[:line]]).to eql 21
expect(frame[Rbkit::MESSAGE_FIELDS[:singleton_method]]).to eql 1
expect(frame[Rbkit::MESSAGE_FIELDS[:thread_id]]).to eql Thread.current.object_id

frame = frames[5]
expect(frame[Rbkit::MESSAGE_FIELDS[:method_name]]).to eql 'foo'
expect(frame[Rbkit::MESSAGE_FIELDS[:label]]).to eql 'SampleClassForTest#foo'
expect(frame[Rbkit::MESSAGE_FIELDS[:file]]).to eql __FILE__
expect(frame[Rbkit::MESSAGE_FIELDS[:line]]).to eql 32
expect(frame[Rbkit::MESSAGE_FIELDS[:file]]).to eql file
expect(frame[Rbkit::MESSAGE_FIELDS[:line]]).to eql 25
expect(frame[Rbkit::MESSAGE_FIELDS[:singleton_method]]).to eql 0
expect(frame[Rbkit::MESSAGE_FIELDS[:thread_id]]).to eql Thread.current.object_id

frame = frames[6]
expect(frame[Rbkit::MESSAGE_FIELDS[:method_name]]).to be_nil
expect(frame[Rbkit::MESSAGE_FIELDS[:label]]).to eql 'block (3 levels) in <top (required)>'
expect(frame[Rbkit::MESSAGE_FIELDS[:file]]).to eql __FILE__
expect(frame[Rbkit::MESSAGE_FIELDS[:line]]).to eql 41
expect(frame[Rbkit::MESSAGE_FIELDS[:line]]).to eql 12
expect(frame[Rbkit::MESSAGE_FIELDS[:singleton_method]]).to eql 0
expect(frame[Rbkit::MESSAGE_FIELDS[:thread_id]]).to eql Thread.current.object_id
end
Expand All @@ -122,33 +97,33 @@ def foo(block)

it 'should record the correct stack frames for cpu instensive operation' do
expect(@messages).to have_message(Rbkit::EVENT_TYPES[:cpu_sample])
expect(@messages).to have_most_cpu_samples_for("#{__FILE__}:17")
expect(@messages).to have_most_cpu_samples_for("#{file}:10")

# Make sure stack trace is correct
frames = most_frequent_sample(@messages)
expect(frames.size).to eql 8

frame = frames[0]
expect(frame[Rbkit::MESSAGE_FIELDS[:method_name]]).to eql 'cpu_intensive_operation'
expect(frame[Rbkit::MESSAGE_FIELDS[:label]]).to eql 'block in RSpec::ExampleGroups::CPUSampling#cpu_intensive_operation'
expect(frame[Rbkit::MESSAGE_FIELDS[:file]]).to eql __FILE__
expect(frame[Rbkit::MESSAGE_FIELDS[:line]]).to eql 17
expect(frame[Rbkit::MESSAGE_FIELDS[:label]]).to eql 'block in Object#cpu_intensive_operation'
expect(frame[Rbkit::MESSAGE_FIELDS[:file]]).to eql file
expect(frame[Rbkit::MESSAGE_FIELDS[:line]]).to eql 10
expect(frame[Rbkit::MESSAGE_FIELDS[:singleton_method]]).to eql 0
expect(frame[Rbkit::MESSAGE_FIELDS[:thread_id]]).to eql Thread.current.object_id

frame = frames[1]
expect(frame[Rbkit::MESSAGE_FIELDS[:method_name]]).to eql 'cpu_intensive_operation'
expect(frame[Rbkit::MESSAGE_FIELDS[:label]]).to eql 'RSpec::ExampleGroups::CPUSampling#cpu_intensive_operation'
expect(frame[Rbkit::MESSAGE_FIELDS[:file]]).to eql __FILE__
expect(frame[Rbkit::MESSAGE_FIELDS[:line]]).to eql 16
expect(frame[Rbkit::MESSAGE_FIELDS[:label]]).to eql 'Object#cpu_intensive_operation'
expect(frame[Rbkit::MESSAGE_FIELDS[:file]]).to eql file
expect(frame[Rbkit::MESSAGE_FIELDS[:line]]).to eql 9
expect(frame[Rbkit::MESSAGE_FIELDS[:singleton_method]]).to eql 0
expect(frame[Rbkit::MESSAGE_FIELDS[:thread_id]]).to eql Thread.current.object_id

frame = frames[2]
expect(frame[Rbkit::MESSAGE_FIELDS[:method_name]]).to be_nil
expect(frame[Rbkit::MESSAGE_FIELDS[:label]]).to eql 'block (4 levels) in <top (required)>'
expect(frame[Rbkit::MESSAGE_FIELDS[:file]]).to eql __FILE__
expect(frame[Rbkit::MESSAGE_FIELDS[:line]]).to eql 121
expect(frame[Rbkit::MESSAGE_FIELDS[:line]]).to eql 96
expect(frame[Rbkit::MESSAGE_FIELDS[:singleton_method]]).to eql 0
expect(frame[Rbkit::MESSAGE_FIELDS[:thread_id]]).to eql Thread.current.object_id

Expand All @@ -163,32 +138,60 @@ def foo(block)
frame = frames[4]
expect(frame[Rbkit::MESSAGE_FIELDS[:method_name]]).to eql 'baz'
expect(frame[Rbkit::MESSAGE_FIELDS[:label]]).to eql 'SampleClassForTest::Sample2#baz'
expect(frame[Rbkit::MESSAGE_FIELDS[:file]]).to eql __FILE__
expect(frame[Rbkit::MESSAGE_FIELDS[:line]]).to eql 22
expect(frame[Rbkit::MESSAGE_FIELDS[:file]]).to eql file
expect(frame[Rbkit::MESSAGE_FIELDS[:line]]).to eql 15
expect(frame[Rbkit::MESSAGE_FIELDS[:singleton_method]]).to eql 0
expect(frame[Rbkit::MESSAGE_FIELDS[:thread_id]]).to eql Thread.current.object_id

frame = frames[5]
expect(frame[Rbkit::MESSAGE_FIELDS[:method_name]]).to eql 'bar'
expect(frame[Rbkit::MESSAGE_FIELDS[:label]]).to eql 'SampleClassForTest.bar'
expect(frame[Rbkit::MESSAGE_FIELDS[:file]]).to eql __FILE__
expect(frame[Rbkit::MESSAGE_FIELDS[:line]]).to eql 28
expect(frame[Rbkit::MESSAGE_FIELDS[:file]]).to eql file
expect(frame[Rbkit::MESSAGE_FIELDS[:line]]).to eql 21
expect(frame[Rbkit::MESSAGE_FIELDS[:singleton_method]]).to eql 1
expect(frame[Rbkit::MESSAGE_FIELDS[:thread_id]]).to eql Thread.current.object_id

frame = frames[6]
expect(frame[Rbkit::MESSAGE_FIELDS[:method_name]]).to eql 'foo'
expect(frame[Rbkit::MESSAGE_FIELDS[:label]]).to eql 'SampleClassForTest#foo'
expect(frame[Rbkit::MESSAGE_FIELDS[:file]]).to eql __FILE__
expect(frame[Rbkit::MESSAGE_FIELDS[:line]]).to eql 32
expect(frame[Rbkit::MESSAGE_FIELDS[:file]]).to eql file
expect(frame[Rbkit::MESSAGE_FIELDS[:line]]).to eql 25
expect(frame[Rbkit::MESSAGE_FIELDS[:singleton_method]]).to eql 0
expect(frame[Rbkit::MESSAGE_FIELDS[:thread_id]]).to eql Thread.current.object_id

frame = frames[7]
expect(frame[Rbkit::MESSAGE_FIELDS[:method_name]]).to be_nil
expect(frame[Rbkit::MESSAGE_FIELDS[:label]]).to eql 'block (3 levels) in <top (required)>'
expect(frame[Rbkit::MESSAGE_FIELDS[:file]]).to eql __FILE__
expect(frame[Rbkit::MESSAGE_FIELDS[:line]]).to eql 41
expect(frame[Rbkit::MESSAGE_FIELDS[:line]]).to eql 12
expect(frame[Rbkit::MESSAGE_FIELDS[:singleton_method]]).to eql 0
expect(frame[Rbkit::MESSAGE_FIELDS[:thread_id]]).to eql Thread.current.object_id
end
end

context 'when stack depth is specified' do
let(:stack_depth) { 2 }
let(:clock_type) { :cpu }
let(:operation) { lambda{ io_intensive_operation; cpu_intensive_operation; } }

it 'should record only that many number of frames as specified stack depth' do
expect(@messages).to have_message(Rbkit::EVENT_TYPES[:cpu_sample])

frames = most_frequent_sample(@messages)
expect(frames.size).to eql stack_depth

frame = frames[0]
expect(frame[Rbkit::MESSAGE_FIELDS[:method_name]]).to eql 'cpu_intensive_operation'
expect(frame[Rbkit::MESSAGE_FIELDS[:label]]).to eql 'block in Object#cpu_intensive_operation'
expect(frame[Rbkit::MESSAGE_FIELDS[:file]]).to eql file
expect(frame[Rbkit::MESSAGE_FIELDS[:line]]).to eql 10
expect(frame[Rbkit::MESSAGE_FIELDS[:singleton_method]]).to eql 0

frame = frames[1]
expect(frame[Rbkit::MESSAGE_FIELDS[:method_name]]).to eql 'cpu_intensive_operation'
expect(frame[Rbkit::MESSAGE_FIELDS[:label]]).to eql 'Object#cpu_intensive_operation'
expect(frame[Rbkit::MESSAGE_FIELDS[:file]]).to eql file
expect(frame[Rbkit::MESSAGE_FIELDS[:line]]).to eql 9
expect(frame[Rbkit::MESSAGE_FIELDS[:singleton_method]]).to eql 0
expect(frame[Rbkit::MESSAGE_FIELDS[:thread_id]]).to eql Thread.current.object_id
end
Expand Down
Loading