summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--yjit.c4
-rw-r--r--yjit.rb45
-rw-r--r--yjit/src/stats.rs62
3 files changed, 84 insertions, 27 deletions
diff --git a/yjit.c b/yjit.c
index 108a376282..daf608b106 100644
--- a/yjit.c
+++ b/yjit.c
@@ -107,6 +107,10 @@ rb_yjit_add_frame(VALUE hash, VALUE frame)
rb_hash_aset(frame_info, ID2SYM(rb_intern("name")), name);
rb_hash_aset(frame_info, ID2SYM(rb_intern("file")), file);
+ rb_hash_aset(frame_info, ID2SYM(rb_intern("samples")), INT2NUM(0));
+ rb_hash_aset(frame_info, ID2SYM(rb_intern("total_samples")), INT2NUM(0));
+ rb_hash_aset(frame_info, ID2SYM(rb_intern("edges")), rb_hash_new());
+ rb_hash_aset(frame_info, ID2SYM(rb_intern("lines")), rb_hash_new());
if (line != INT2FIX(0)) {
rb_hash_aset(frame_info, ID2SYM(rb_intern("line")), line);
diff --git a/yjit.rb b/yjit.rb
index 15c84d0c61..226f2a8134 100644
--- a/yjit.rb
+++ b/yjit.rb
@@ -41,16 +41,11 @@ module RubyVM::YJIT
frames = results[:frames].dup
samples_count = 0
- frames.each do |frame_id, frame|
- frame[:samples] = 0
- frame[:edges] = {}
- end
-
# Loop through the instructions and set the frame hash with the data.
# We use nonexistent.def for the file name, otherwise insns.def will be displayed
# and that information isn't useful in this context.
RubyVM::INSTRUCTION_NAMES.each_with_index do |name, frame_id|
- frame_hash = { samples: 0, total_samples: 0, edges: {}, name: name, file: "nonexistent.def", line: nil }
+ frame_hash = { samples: 0, total_samples: 0, edges: {}, name: name, file: "nonexistent.def", line: nil, lines: {} }
results[:frames][frame_id] = frame_hash
frames[frame_id] = frame_hash
end
@@ -58,12 +53,22 @@ module RubyVM::YJIT
# Loop through the raw_samples and build the hashes for StackProf.
# The loop is based off an example in the StackProf documentation and therefore
# this functionality can only work with that library.
- while raw_samples.length > 0
- stack_trace = raw_samples.shift(raw_samples.shift + 1)
- lines = line_samples.shift(line_samples.shift + 1)
+ #
+ # Raw Samples:
+ # [ length, frame1, frame2, frameN, ..., instruction, count
+ #
+ # Line Samples
+ # [ length, line_1, line_2, line_n, ..., dummy value, count
+ i = 0
+ while i < raw_samples.length
+ stack_length = raw_samples[i] + 1
+ i += 1 # consume the stack length
+
prev_frame_id = nil
+ stack_length.times do |idx|
+ idx += i
+ frame_id = raw_samples[idx]
- stack_trace.each_with_index do |frame_id, idx|
if prev_frame_id
prev_frame = frames[prev_frame_id]
prev_frame[:edges][frame_id] ||= 0
@@ -71,26 +76,28 @@ module RubyVM::YJIT
end
frame_info = frames[frame_id]
- frame_info[:total_samples] ||= 0
frame_info[:total_samples] += 1
- frame_info[:lines] ||= {}
- frame_info[:lines][lines[idx]] ||= [0, 0]
- frame_info[:lines][lines[idx]][0] += 1
+ frame_info[:lines][line_samples[idx]] ||= [0, 0]
+ frame_info[:lines][line_samples[idx]][0] += 1
prev_frame_id = frame_id
end
- top_frame_id = stack_trace.last
+ i += stack_length # consume the stack
+
+ top_frame_id = prev_frame_id
top_frame_line = 1
- frames[top_frame_id][:samples] += 1
+ sample_count = raw_samples[i]
+
+ frames[top_frame_id][:samples] += sample_count
frames[top_frame_id][:lines] ||= {}
frames[top_frame_id][:lines][top_frame_line] ||= [0, 0]
- frames[top_frame_id][:lines][top_frame_line][1] += 1
+ frames[top_frame_id][:lines][top_frame_line][1] += sample_count
- samples_count += raw_samples.shift
- line_samples.shift
+ samples_count += sample_count
+ i += 1
end
results[:samples] = samples_count
diff --git a/yjit/src/stats.rs b/yjit/src/stats.rs
index 4118f894b4..4843cecf92 100644
--- a/yjit/src/stats.rs
+++ b/yjit/src/stats.rs
@@ -432,23 +432,67 @@ pub extern "C" fn rb_yjit_record_exit_stack(exit_pc: *const VALUE)
// Call frame info is stored in the frames_buffer, line number information
// in the lines_buffer. The first argument is the start point and the second
// argument is the buffer limit, set at 2048.
- let num = unsafe { rb_profile_frames(0, BUFF_LEN as i32, frames_buffer.as_mut_ptr(), lines_buffer.as_mut_ptr()) };
+ let stack_length = unsafe { rb_profile_frames(0, BUFF_LEN as i32, frames_buffer.as_mut_ptr(), lines_buffer.as_mut_ptr()) };
+ let samples_length = (stack_length as usize) + 3;
- let mut i = num - 1;
let yjit_raw_samples = YjitExitLocations::get_raw_samples();
let yjit_line_samples = YjitExitLocations::get_line_samples();
- yjit_raw_samples.push(VALUE(num as usize));
- yjit_line_samples.push(num);
+ // If yjit_raw_samples is less than or equal to the current length of the samples
+ // we might have seen this stack trace previously.
+ if yjit_raw_samples.len() >= samples_length {
+ let prev_stack_len_index = yjit_raw_samples.len() - samples_length;
+ let prev_stack_len = i64::from(yjit_raw_samples[prev_stack_len_index]);
+ let mut idx = stack_length - 1;
+ let mut prev_frame_idx = 0;
+ let mut seen_already = true;
+
+ // If the previous stack lenght and current stack length are equal,
+ // loop and compare the current frame to the previous frame. If they are
+ // not equal, set seen_already to false and break out of the loop.
+ if prev_stack_len == stack_length as i64 {
+ while idx >= 0 {
+ let current_frame = frames_buffer[idx as usize];
+ let prev_frame = yjit_raw_samples[prev_stack_len_index + prev_frame_idx + 1];
+
+ // If the current frame and previous frame are not equal, set
+ // seen_already to false and break out of the loop.
+ if current_frame != prev_frame {
+ seen_already = false;
+ break;
+ }
+
+ idx -= 1;
+ prev_frame_idx += 1;
+ }
+
+ // If we know we've seen this stack before, increment the counter by 1.
+ if seen_already {
+ let prev_idx = yjit_raw_samples.len() - 1;
+ let prev_count = i64::from(yjit_raw_samples[prev_idx]);
+ let new_count = prev_count + 1;
+
+ yjit_raw_samples[prev_idx] = VALUE(new_count as usize);
+ yjit_line_samples[prev_idx] = new_count as i32;
+
+ return;
+ }
+ }
+ }
+
+ yjit_raw_samples.push(VALUE(stack_length as usize));
+ yjit_line_samples.push(stack_length);
+
+ let mut idx = stack_length - 1;
- while i >= 0 {
- let frame = frames_buffer[i as usize];
- let line = lines_buffer[i as usize];
+ while idx >= 0 {
+ let frame = frames_buffer[idx as usize];
+ let line = lines_buffer[idx as usize];
yjit_raw_samples.push(frame);
yjit_line_samples.push(line);
- i -= 1;
+ idx -= 1;
}
// Push the insn value into the yjit_raw_samples Vec.
@@ -459,6 +503,8 @@ pub extern "C" fn rb_yjit_record_exit_stack(exit_pc: *const VALUE)
let line = yjit_line_samples.len() - 1;
yjit_line_samples.push(line as i32);
+ // Push number of times seen onto the stack, which is 1
+ // because it's the first time we've seen it.
yjit_raw_samples.push(VALUE(1 as usize));
yjit_line_samples.push(1);
}