RJIT: Implement --rjit-trace-exits
This commit is contained in:
parent
bbd9221e46
commit
9cd5441d28
@ -6,12 +6,12 @@ module RubyVM::RJIT
|
|||||||
# @param pc [Integer]
|
# @param pc [Integer]
|
||||||
# @param asm [RubyVM::RJIT::Assembler]
|
# @param asm [RubyVM::RJIT::Assembler]
|
||||||
def compile_entry_exit(pc, ctx, asm, cause:)
|
def compile_entry_exit(pc, ctx, asm, cause:)
|
||||||
# Increment per-insn exit counter
|
|
||||||
incr_insn_exit(pc, asm)
|
|
||||||
|
|
||||||
# Fix pc/sp offsets for the interpreter
|
# Fix pc/sp offsets for the interpreter
|
||||||
save_pc_and_sp(pc, ctx, asm, reset_sp_offset: false)
|
save_pc_and_sp(pc, ctx, asm, reset_sp_offset: false)
|
||||||
|
|
||||||
|
# Increment per-insn exit counter
|
||||||
|
count_insn_exit(pc, asm)
|
||||||
|
|
||||||
# Restore callee-saved registers
|
# Restore callee-saved registers
|
||||||
asm.comment("#{cause}: entry exit")
|
asm.comment("#{cause}: entry exit")
|
||||||
asm.pop(SP)
|
asm.pop(SP)
|
||||||
@ -62,12 +62,12 @@ module RubyVM::RJIT
|
|||||||
# @param ctx [RubyVM::RJIT::Context]
|
# @param ctx [RubyVM::RJIT::Context]
|
||||||
# @param asm [RubyVM::RJIT::Assembler]
|
# @param asm [RubyVM::RJIT::Assembler]
|
||||||
def compile_side_exit(pc, ctx, asm)
|
def compile_side_exit(pc, ctx, asm)
|
||||||
# Increment per-insn exit counter
|
|
||||||
incr_insn_exit(pc, asm)
|
|
||||||
|
|
||||||
# Fix pc/sp offsets for the interpreter
|
# Fix pc/sp offsets for the interpreter
|
||||||
save_pc_and_sp(pc, ctx.dup, asm) # dup to avoid sp_offset update
|
save_pc_and_sp(pc, ctx.dup, asm) # dup to avoid sp_offset update
|
||||||
|
|
||||||
|
# Increment per-insn exit counter
|
||||||
|
count_insn_exit(pc, asm)
|
||||||
|
|
||||||
# Restore callee-saved registers
|
# Restore callee-saved registers
|
||||||
asm.comment("exit to interpreter on #{pc_to_insn(pc).name}")
|
asm.comment("exit to interpreter on #{pc_to_insn(pc).name}")
|
||||||
asm.pop(SP)
|
asm.pop(SP)
|
||||||
@ -105,13 +105,18 @@ module RubyVM::RJIT
|
|||||||
|
|
||||||
# @param pc [Integer]
|
# @param pc [Integer]
|
||||||
# @param asm [RubyVM::RJIT::Assembler]
|
# @param asm [RubyVM::RJIT::Assembler]
|
||||||
def incr_insn_exit(pc, asm)
|
def count_insn_exit(pc, asm)
|
||||||
if C.rjit_opts.stats
|
if C.rjit_opts.stats
|
||||||
insn = Compiler.decode_insn(C.VALUE.new(pc).*)
|
insn = Compiler.decode_insn(C.VALUE.new(pc).*)
|
||||||
asm.comment("increment insn exit: #{insn.name}")
|
asm.comment("increment insn exit: #{insn.name}")
|
||||||
asm.mov(:rax, (C.rjit_insn_exits + insn.bin).to_i)
|
asm.mov(:rax, (C.rjit_insn_exits + insn.bin).to_i)
|
||||||
asm.add([:rax], 1) # TODO: lock
|
asm.add([:rax], 1) # TODO: lock
|
||||||
end
|
end
|
||||||
|
if C.rjit_opts.trace_exits
|
||||||
|
asm.comment('rjit_record_exit_stack')
|
||||||
|
asm.mov(C_ARGS[0], pc)
|
||||||
|
asm.call(C.rjit_record_exit_stack)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# @param jit [RubyVM::RJIT::JITState]
|
# @param jit [RubyVM::RJIT::JITState]
|
||||||
|
@ -30,6 +30,7 @@ module RubyVM::RJIT
|
|||||||
class << self
|
class << self
|
||||||
private
|
private
|
||||||
|
|
||||||
|
# --yjit-stats at_exit
|
||||||
def print_stats
|
def print_stats
|
||||||
stats = runtime_stats
|
stats = runtime_stats
|
||||||
$stderr.puts("***RJIT: Printing RJIT statistics on exit***")
|
$stderr.puts("***RJIT: Printing RJIT statistics on exit***")
|
||||||
@ -98,5 +99,87 @@ module RubyVM::RJIT
|
|||||||
with_commas = d_groups.map(&:join).join(',').reverse
|
with_commas = d_groups.map(&:join).join(',').reverse
|
||||||
[with_commas, decimal].compact.join('.').rjust(pad, ' ')
|
[with_commas, decimal].compact.join('.').rjust(pad, ' ')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# --yjit-trace-exits at_exit
|
||||||
|
def dump_trace_exits
|
||||||
|
filename = "#{Dir.pwd}/rjit_exit_locations.dump"
|
||||||
|
File.binwrite(filename, Marshal.dump(exit_traces))
|
||||||
|
$stderr.puts("RJIT exit locations dumped to:\n#{filename}")
|
||||||
|
end
|
||||||
|
|
||||||
|
# Convert rb_rjit_raw_samples and rb_rjit_line_samples into a StackProf format.
|
||||||
|
def exit_traces
|
||||||
|
results = C.rjit_exit_traces
|
||||||
|
raw_samples = results[:raw].dup
|
||||||
|
line_samples = results[:lines].dup
|
||||||
|
frames = results[:frames].dup
|
||||||
|
samples_count = 0
|
||||||
|
|
||||||
|
# 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, lines: {} }
|
||||||
|
results[:frames][frame_id] = frame_hash
|
||||||
|
frames[frame_id] = frame_hash
|
||||||
|
end
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
# 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]
|
||||||
|
|
||||||
|
if prev_frame_id
|
||||||
|
prev_frame = frames[prev_frame_id]
|
||||||
|
prev_frame[:edges][frame_id] ||= 0
|
||||||
|
prev_frame[:edges][frame_id] += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
frame_info = frames[frame_id]
|
||||||
|
frame_info[:total_samples] += 1
|
||||||
|
|
||||||
|
frame_info[:lines][line_samples[idx]] ||= [0, 0]
|
||||||
|
frame_info[:lines][line_samples[idx]][0] += 1
|
||||||
|
|
||||||
|
prev_frame_id = frame_id
|
||||||
|
end
|
||||||
|
|
||||||
|
i += stack_length # consume the stack
|
||||||
|
|
||||||
|
top_frame_id = prev_frame_id
|
||||||
|
top_frame_line = 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] += sample_count
|
||||||
|
|
||||||
|
samples_count += sample_count
|
||||||
|
i += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
results[:samples] = samples_count
|
||||||
|
# Set missed_samples and gc_samples to 0 as their values
|
||||||
|
# don't matter to us in this context.
|
||||||
|
results[:missed_samples] = 0
|
||||||
|
results[:gc_samples] = 0
|
||||||
|
results
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
27
rjit.c
27
rjit.c
@ -67,7 +67,10 @@ struct rjit_options rb_rjit_opts;
|
|||||||
|
|
||||||
// true if RJIT is enabled.
|
// true if RJIT is enabled.
|
||||||
bool rb_rjit_enabled = false;
|
bool rb_rjit_enabled = false;
|
||||||
|
// true if --rjit-stats (used before rb_rjit_opts is set)
|
||||||
bool rb_rjit_stats_enabled = false;
|
bool rb_rjit_stats_enabled = false;
|
||||||
|
// true if --rjit-trace-exits (used before rb_rjit_opts is set)
|
||||||
|
bool rb_rjit_trace_exits_enabled = false;
|
||||||
// true if JIT-ed code should be called. When `ruby_vm_event_enabled_global_flags & ISEQ_TRACE_EVENTS`
|
// true if JIT-ed code should be called. When `ruby_vm_event_enabled_global_flags & ISEQ_TRACE_EVENTS`
|
||||||
// and `rb_rjit_call_p == false`, any JIT-ed code execution is cancelled as soon as possible.
|
// and `rb_rjit_call_p == false`, any JIT-ed code execution is cancelled as soon as possible.
|
||||||
bool rb_rjit_call_p = false;
|
bool rb_rjit_call_p = false;
|
||||||
@ -93,6 +96,11 @@ static VALUE rb_cRJITCfpPtr = 0;
|
|||||||
// RubyVM::RJIT::Hooks
|
// RubyVM::RJIT::Hooks
|
||||||
static VALUE rb_mRJITHooks = 0;
|
static VALUE rb_mRJITHooks = 0;
|
||||||
|
|
||||||
|
// Frames for --rjit-trace-exits
|
||||||
|
VALUE rb_rjit_raw_samples = 0;
|
||||||
|
// Line numbers for --rjit-trace-exits
|
||||||
|
VALUE rb_rjit_line_samples = 0;
|
||||||
|
|
||||||
// A default threshold used to add iseq to JIT.
|
// A default threshold used to add iseq to JIT.
|
||||||
#define DEFAULT_CALL_THRESHOLD 30
|
#define DEFAULT_CALL_THRESHOLD 30
|
||||||
// Size of executable memory block in MiB.
|
// Size of executable memory block in MiB.
|
||||||
@ -113,6 +121,9 @@ rb_rjit_setup_options(const char *s, struct rjit_options *rjit_opt)
|
|||||||
else if (opt_match_noarg(s, l, "stats")) {
|
else if (opt_match_noarg(s, l, "stats")) {
|
||||||
rjit_opt->stats = true;
|
rjit_opt->stats = true;
|
||||||
}
|
}
|
||||||
|
else if (opt_match_noarg(s, l, "trace-exits")) {
|
||||||
|
rjit_opt->trace_exits = true;
|
||||||
|
}
|
||||||
else if (opt_match_arg(s, l, "call-threshold")) {
|
else if (opt_match_arg(s, l, "call-threshold")) {
|
||||||
rjit_opt->call_threshold = atoi(s + 1);
|
rjit_opt->call_threshold = atoi(s + 1);
|
||||||
}
|
}
|
||||||
@ -136,6 +147,7 @@ rb_rjit_setup_options(const char *s, struct rjit_options *rjit_opt)
|
|||||||
const struct ruby_opt_message rb_rjit_option_messages[] = {
|
const struct ruby_opt_message rb_rjit_option_messages[] = {
|
||||||
#if RJIT_STATS
|
#if RJIT_STATS
|
||||||
M("--rjit-stats", "", "Enable collecting RJIT statistics"),
|
M("--rjit-stats", "", "Enable collecting RJIT statistics"),
|
||||||
|
M("--rjit-trace-exits", "", "Trace side exit locations"),
|
||||||
#endif
|
#endif
|
||||||
M("--rjit-exec-mem-size=num", "", "Size of executable memory block in MiB (default: " STRINGIZE(DEFAULT_EXEC_MEM_SIZE) ")"),
|
M("--rjit-exec-mem-size=num", "", "Size of executable memory block in MiB (default: " STRINGIZE(DEFAULT_EXEC_MEM_SIZE) ")"),
|
||||||
M("--rjit-call-threshold=num", "", "Number of calls to trigger JIT (default: " STRINGIZE(DEFAULT_CALL_THRESHOLD) ")"),
|
M("--rjit-call-threshold=num", "", "Number of calls to trigger JIT (default: " STRINGIZE(DEFAULT_CALL_THRESHOLD) ")"),
|
||||||
@ -314,6 +326,8 @@ rb_rjit_mark(void)
|
|||||||
rb_gc_mark(rb_cRJITIseqPtr);
|
rb_gc_mark(rb_cRJITIseqPtr);
|
||||||
rb_gc_mark(rb_cRJITCfpPtr);
|
rb_gc_mark(rb_cRJITCfpPtr);
|
||||||
rb_gc_mark(rb_mRJITHooks);
|
rb_gc_mark(rb_mRJITHooks);
|
||||||
|
rb_gc_mark(rb_rjit_raw_samples);
|
||||||
|
rb_gc_mark(rb_rjit_line_samples);
|
||||||
|
|
||||||
RUBY_MARK_LEAVE("rjit");
|
RUBY_MARK_LEAVE("rjit");
|
||||||
}
|
}
|
||||||
@ -398,6 +412,10 @@ rb_rjit_init(const struct rjit_options *opts)
|
|||||||
rb_cRJITIseqPtr = rb_funcall(rb_mRJITC, rb_intern("rb_iseq_t"), 0);
|
rb_cRJITIseqPtr = rb_funcall(rb_mRJITC, rb_intern("rb_iseq_t"), 0);
|
||||||
rb_cRJITCfpPtr = rb_funcall(rb_mRJITC, rb_intern("rb_control_frame_t"), 0);
|
rb_cRJITCfpPtr = rb_funcall(rb_mRJITC, rb_intern("rb_control_frame_t"), 0);
|
||||||
rb_mRJITHooks = rb_const_get(rb_mRJIT, rb_intern("Hooks"));
|
rb_mRJITHooks = rb_const_get(rb_mRJIT, rb_intern("Hooks"));
|
||||||
|
if (rb_rjit_opts.trace_exits) {
|
||||||
|
rb_rjit_raw_samples = rb_ary_new();
|
||||||
|
rb_rjit_line_samples = rb_ary_new();
|
||||||
|
}
|
||||||
|
|
||||||
// Enable RJIT and stats from here
|
// Enable RJIT and stats from here
|
||||||
rb_rjit_call_p = !rb_rjit_opts.pause;
|
rb_rjit_call_p = !rb_rjit_opts.pause;
|
||||||
@ -408,13 +426,20 @@ rb_rjit_init(const struct rjit_options *opts)
|
|||||||
// Primitive for rjit.rb
|
// Primitive for rjit.rb
|
||||||
//
|
//
|
||||||
|
|
||||||
// Same as `RubyVM::RJIT::C.enabled?`, but this is used before rjit_init.
|
// Same as `rb_rjit_opts.stats`, but this is used before rb_rjit_opts is set.
|
||||||
static VALUE
|
static VALUE
|
||||||
rjit_stats_enabled_p(rb_execution_context_t *ec, VALUE self)
|
rjit_stats_enabled_p(rb_execution_context_t *ec, VALUE self)
|
||||||
{
|
{
|
||||||
return RBOOL(rb_rjit_stats_enabled);
|
return RBOOL(rb_rjit_stats_enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Same as `rb_rjit_opts.trace_exits`, but this is used before rb_rjit_opts is set.
|
||||||
|
static VALUE
|
||||||
|
rjit_trace_exits_enabled_p(rb_execution_context_t *ec, VALUE self)
|
||||||
|
{
|
||||||
|
return RBOOL(rb_rjit_trace_exits_enabled);
|
||||||
|
}
|
||||||
|
|
||||||
// Disable anything that could impact stats. It ends up disabling JIT calls as well.
|
// Disable anything that could impact stats. It ends up disabling JIT calls as well.
|
||||||
static VALUE
|
static VALUE
|
||||||
rjit_stop_stats(rb_execution_context_t *ec, VALUE self)
|
rjit_stop_stats(rb_execution_context_t *ec, VALUE self)
|
||||||
|
4
rjit.h
4
rjit.h
@ -32,6 +32,8 @@ struct rjit_options {
|
|||||||
unsigned int exec_mem_size;
|
unsigned int exec_mem_size;
|
||||||
// Collect RJIT statistics
|
// Collect RJIT statistics
|
||||||
bool stats;
|
bool stats;
|
||||||
|
// Trace side exit locations
|
||||||
|
bool trace_exits;
|
||||||
// Enable disasm of all JIT code
|
// Enable disasm of all JIT code
|
||||||
bool dump_disasm;
|
bool dump_disasm;
|
||||||
// [experimental] Do not start RJIT until RJIT.resume is called.
|
// [experimental] Do not start RJIT until RJIT.resume is called.
|
||||||
@ -69,6 +71,7 @@ extern void rb_rjit_collect_vm_usage_insn(int insn);
|
|||||||
|
|
||||||
extern bool rb_rjit_enabled;
|
extern bool rb_rjit_enabled;
|
||||||
extern bool rb_rjit_stats_enabled;
|
extern bool rb_rjit_stats_enabled;
|
||||||
|
extern bool rb_rjit_trace_exits_enabled;
|
||||||
|
|
||||||
# else // USE_RJIT
|
# else // USE_RJIT
|
||||||
|
|
||||||
@ -88,6 +91,7 @@ static inline void rb_rjit_tracing_invalidate_all(rb_event_flag_t new_iseq_event
|
|||||||
#define rb_rjit_enabled false
|
#define rb_rjit_enabled false
|
||||||
#define rb_rjit_call_p false
|
#define rb_rjit_call_p false
|
||||||
#define rb_rjit_stats_enabled false
|
#define rb_rjit_stats_enabled false
|
||||||
|
#define rb_rjit_trace_exits_enabled false
|
||||||
|
|
||||||
#define rb_rjit_call_threshold() UINT_MAX
|
#define rb_rjit_call_threshold() UINT_MAX
|
||||||
|
|
||||||
|
6
rjit.rb
6
rjit.rb
@ -18,6 +18,12 @@ module RubyVM::RJIT
|
|||||||
print_stats
|
print_stats
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
if Primitive.rjit_trace_exits_enabled_p
|
||||||
|
at_exit do
|
||||||
|
Primitive.rjit_stop_stats
|
||||||
|
dump_trace_exits
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if RubyVM::RJIT.enabled?
|
if RubyVM::RJIT.enabled?
|
||||||
|
193
rjit_c.c
193
rjit_c.c
@ -11,6 +11,8 @@
|
|||||||
#if USE_RJIT
|
#if USE_RJIT
|
||||||
|
|
||||||
#include "rjit_c.h"
|
#include "rjit_c.h"
|
||||||
|
#include "include/ruby/assert.h"
|
||||||
|
#include "include/ruby/debug.h"
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
#include "internal/compile.h"
|
#include "internal/compile.h"
|
||||||
#include "internal/fixnum.h"
|
#include "internal/fixnum.h"
|
||||||
@ -206,6 +208,197 @@ rjit_get_proc_ptr(VALUE procv)
|
|||||||
return proc;
|
return proc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Use the same buffer size as Stackprof.
|
||||||
|
#define BUFF_LEN 2048
|
||||||
|
|
||||||
|
extern VALUE rb_rjit_raw_samples;
|
||||||
|
extern VALUE rb_rjit_line_samples;
|
||||||
|
|
||||||
|
static void
|
||||||
|
rjit_record_exit_stack(const VALUE *exit_pc)
|
||||||
|
{
|
||||||
|
// Let Primitive.rjit_stop_stats stop this
|
||||||
|
if (!rb_rjit_call_p) return;
|
||||||
|
|
||||||
|
// Get the opcode from the encoded insn handler at this PC
|
||||||
|
int insn = rb_vm_insn_addr2opcode((void *)*exit_pc);
|
||||||
|
|
||||||
|
// Create 2 array buffers to be used to collect frames and lines.
|
||||||
|
VALUE frames_buffer[BUFF_LEN] = { 0 };
|
||||||
|
int lines_buffer[BUFF_LEN] = { 0 };
|
||||||
|
|
||||||
|
// Records call frame and line information for each method entry into two
|
||||||
|
// temporary buffers. Returns the number of times we added to the buffer (ie
|
||||||
|
// the length of the stack).
|
||||||
|
//
|
||||||
|
// 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.
|
||||||
|
int stack_length = rb_profile_frames(0, BUFF_LEN, frames_buffer, lines_buffer);
|
||||||
|
int samples_length = stack_length + 3; // 3: length, insn, count
|
||||||
|
|
||||||
|
// If yjit_raw_samples is less than or equal to the current length of the samples
|
||||||
|
// we might have seen this stack trace previously.
|
||||||
|
int prev_stack_len_index = RARRAY_LEN(rb_rjit_raw_samples) - samples_length;
|
||||||
|
VALUE prev_stack_len_obj;
|
||||||
|
if (RARRAY_LEN(rb_rjit_raw_samples) >= samples_length && FIXNUM_P(prev_stack_len_obj = RARRAY_AREF(rb_rjit_raw_samples, prev_stack_len_index))) {
|
||||||
|
int prev_stack_len = NUM2INT(prev_stack_len_obj);
|
||||||
|
int idx = stack_length - 1;
|
||||||
|
int prev_frame_idx = 0;
|
||||||
|
bool seen_already = true;
|
||||||
|
|
||||||
|
// If the previous stack length 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) {
|
||||||
|
while (idx >= 0) {
|
||||||
|
VALUE current_frame = frames_buffer[idx];
|
||||||
|
VALUE prev_frame = RARRAY_AREF(rb_rjit_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--;
|
||||||
|
prev_frame_idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we know we've seen this stack before, increment the counter by 1.
|
||||||
|
if (seen_already) {
|
||||||
|
int prev_idx = RARRAY_LEN(rb_rjit_raw_samples) - 1;
|
||||||
|
int prev_count = NUM2INT(RARRAY_AREF(rb_rjit_raw_samples, prev_idx));
|
||||||
|
int new_count = prev_count + 1;
|
||||||
|
|
||||||
|
rb_ary_store(rb_rjit_raw_samples, prev_idx, INT2NUM(new_count));
|
||||||
|
rb_ary_store(rb_rjit_line_samples, prev_idx, INT2NUM(new_count));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rb_ary_push(rb_rjit_raw_samples, INT2NUM(stack_length));
|
||||||
|
rb_ary_push(rb_rjit_line_samples, INT2NUM(stack_length));
|
||||||
|
|
||||||
|
int idx = stack_length - 1;
|
||||||
|
|
||||||
|
while (idx >= 0) {
|
||||||
|
VALUE frame = frames_buffer[idx];
|
||||||
|
int line = lines_buffer[idx];
|
||||||
|
|
||||||
|
rb_ary_push(rb_rjit_raw_samples, frame);
|
||||||
|
rb_ary_push(rb_rjit_line_samples, INT2NUM(line));
|
||||||
|
|
||||||
|
idx--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Push the insn value into the yjit_raw_samples Vec.
|
||||||
|
rb_ary_push(rb_rjit_raw_samples, INT2NUM(insn));
|
||||||
|
|
||||||
|
// Push the current line onto the yjit_line_samples Vec. This
|
||||||
|
// points to the line in insns.def.
|
||||||
|
int line = RARRAY_LEN(rb_rjit_line_samples) - 1;
|
||||||
|
rb_ary_push(rb_rjit_line_samples, INT2NUM(line));
|
||||||
|
|
||||||
|
// Push number of times seen onto the stack, which is 1
|
||||||
|
// because it's the first time we've seen it.
|
||||||
|
rb_ary_push(rb_rjit_raw_samples, INT2NUM(1));
|
||||||
|
rb_ary_push(rb_rjit_line_samples, INT2NUM(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
// For a given raw_sample (frame), set the hash with the caller's
|
||||||
|
// name, file, and line number. Return the hash with collected frame_info.
|
||||||
|
static void
|
||||||
|
rjit_add_frame(VALUE hash, VALUE frame)
|
||||||
|
{
|
||||||
|
VALUE frame_id = SIZET2NUM(frame);
|
||||||
|
|
||||||
|
if (RTEST(rb_hash_aref(hash, frame_id))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
VALUE frame_info = rb_hash_new();
|
||||||
|
// Full label for the frame
|
||||||
|
VALUE name = rb_profile_frame_full_label(frame);
|
||||||
|
// Absolute path of the frame from rb_iseq_realpath
|
||||||
|
VALUE file = rb_profile_frame_absolute_path(frame);
|
||||||
|
// Line number of the frame
|
||||||
|
VALUE line = rb_profile_frame_first_lineno(frame);
|
||||||
|
|
||||||
|
// If absolute path isn't available use the rb_iseq_path
|
||||||
|
if (NIL_P(file)) {
|
||||||
|
file = rb_profile_frame_path(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
rb_hash_aset(hash, frame_id, frame_info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static VALUE
|
||||||
|
rjit_exit_traces(void)
|
||||||
|
{
|
||||||
|
int samples_len = RARRAY_LEN(rb_rjit_raw_samples);
|
||||||
|
RUBY_ASSERT(samples_len == RARRAY_LEN(rb_rjit_line_samples));
|
||||||
|
|
||||||
|
VALUE result = rb_hash_new();
|
||||||
|
VALUE raw_samples = rb_ary_new_capa(samples_len);
|
||||||
|
VALUE line_samples = rb_ary_new_capa(samples_len);
|
||||||
|
VALUE frames = rb_hash_new();
|
||||||
|
int idx = 0;
|
||||||
|
|
||||||
|
// While the index is less than samples_len, parse yjit_raw_samples and
|
||||||
|
// yjit_line_samples, then add casted values to raw_samples and line_samples array.
|
||||||
|
while (idx < samples_len) {
|
||||||
|
int num = NUM2INT(RARRAY_AREF(rb_rjit_raw_samples, idx));
|
||||||
|
int line_num = NUM2INT(RARRAY_AREF(rb_rjit_line_samples, idx));
|
||||||
|
idx++;
|
||||||
|
|
||||||
|
rb_ary_push(raw_samples, SIZET2NUM(num));
|
||||||
|
rb_ary_push(line_samples, INT2NUM(line_num));
|
||||||
|
|
||||||
|
// Loop through the length of samples_len and add data to the
|
||||||
|
// frames hash. Also push the current value onto the raw_samples
|
||||||
|
// and line_samples array respectively.
|
||||||
|
for (int o = 0; o < num; o++) {
|
||||||
|
rjit_add_frame(frames, RARRAY_AREF(rb_rjit_raw_samples, idx));
|
||||||
|
rb_ary_push(raw_samples, SIZET2NUM(RARRAY_AREF(rb_rjit_raw_samples, idx)));
|
||||||
|
rb_ary_push(line_samples, RARRAY_AREF(rb_rjit_line_samples, idx));
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// insn BIN and lineno
|
||||||
|
rb_ary_push(raw_samples, RARRAY_AREF(rb_rjit_raw_samples, idx));
|
||||||
|
rb_ary_push(line_samples, RARRAY_AREF(rb_rjit_line_samples, idx));
|
||||||
|
idx++;
|
||||||
|
|
||||||
|
// Number of times seen
|
||||||
|
rb_ary_push(raw_samples, RARRAY_AREF(rb_rjit_raw_samples, idx));
|
||||||
|
rb_ary_push(line_samples, RARRAY_AREF(rb_rjit_line_samples, idx));
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set add the raw_samples, line_samples, and frames to the results
|
||||||
|
// hash.
|
||||||
|
rb_hash_aset(result, ID2SYM(rb_intern("raw")), raw_samples);
|
||||||
|
rb_hash_aset(result, ID2SYM(rb_intern("lines")), line_samples);
|
||||||
|
rb_hash_aset(result, ID2SYM(rb_intern("frames")), frames);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// An offsetof implementation that works for unnamed struct and union.
|
// An offsetof implementation that works for unnamed struct and union.
|
||||||
// Multiplying 8 for compatibility with libclang's offsetof.
|
// Multiplying 8 for compatibility with libclang's offsetof.
|
||||||
#define OFFSETOF(ptr, member) RB_SIZE2NUM(((char *)&ptr.member - (char*)&ptr) * 8)
|
#define OFFSETOF(ptr, member) RB_SIZE2NUM(((char *)&ptr.member - (char*)&ptr) * 8)
|
||||||
|
@ -294,6 +294,10 @@ module RubyVM::RJIT # :nodoc: all
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def rjit_exit_traces
|
||||||
|
Primitive.cexpr! 'rjit_exit_traces()'
|
||||||
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
# Utilities: Not used by RJIT, but useful for debugging
|
# Utilities: Not used by RJIT, but useful for debugging
|
||||||
#
|
#
|
||||||
@ -585,6 +589,10 @@ module RubyVM::RJIT # :nodoc: all
|
|||||||
Primitive.cexpr! %q{ SIZET2NUM((size_t)rjit_optimized_call) }
|
Primitive.cexpr! %q{ SIZET2NUM((size_t)rjit_optimized_call) }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def C.rjit_record_exit_stack
|
||||||
|
Primitive.cexpr! %q{ SIZET2NUM((size_t)rjit_record_exit_stack) }
|
||||||
|
end
|
||||||
|
|
||||||
def C.rjit_str_neq_internal
|
def C.rjit_str_neq_internal
|
||||||
Primitive.cexpr! %q{ SIZET2NUM((size_t)rjit_str_neq_internal) }
|
Primitive.cexpr! %q{ SIZET2NUM((size_t)rjit_str_neq_internal) }
|
||||||
end
|
end
|
||||||
@ -1239,6 +1247,7 @@ module RubyVM::RJIT # :nodoc: all
|
|||||||
call_threshold: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rjit_options *)NULL)), call_threshold)")],
|
call_threshold: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rjit_options *)NULL)), call_threshold)")],
|
||||||
exec_mem_size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rjit_options *)NULL)), exec_mem_size)")],
|
exec_mem_size: [CType::Immediate.parse("unsigned int"), Primitive.cexpr!("OFFSETOF((*((struct rjit_options *)NULL)), exec_mem_size)")],
|
||||||
stats: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rjit_options *)NULL)), stats)")],
|
stats: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rjit_options *)NULL)), stats)")],
|
||||||
|
trace_exits: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rjit_options *)NULL)), trace_exits)")],
|
||||||
dump_disasm: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rjit_options *)NULL)), dump_disasm)")],
|
dump_disasm: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rjit_options *)NULL)), dump_disasm)")],
|
||||||
pause: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rjit_options *)NULL)), pause)")],
|
pause: [self._Bool, Primitive.cexpr!("OFFSETOF((*((struct rjit_options *)NULL)), pause)")],
|
||||||
)
|
)
|
||||||
|
2
ruby.c
2
ruby.c
@ -1617,6 +1617,8 @@ ruby_opt_init(ruby_cmdline_options_t *opt)
|
|||||||
rb_rjit_enabled = true;
|
rb_rjit_enabled = true;
|
||||||
if (opt->rjit.stats)
|
if (opt->rjit.stats)
|
||||||
rb_rjit_stats_enabled = true;
|
rb_rjit_stats_enabled = true;
|
||||||
|
if (opt->rjit.trace_exits)
|
||||||
|
rb_rjit_trace_exits_enabled = true;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Init_ext(); /* load statically linked extensions before rubygems */
|
Init_ext(); /* load statically linked extensions before rubygems */
|
||||||
|
@ -512,6 +512,7 @@ generator = BindingGenerator.new(
|
|||||||
rjit_full_cfunc_return
|
rjit_full_cfunc_return
|
||||||
rjit_optimized_call
|
rjit_optimized_call
|
||||||
rjit_str_neq_internal
|
rjit_str_neq_internal
|
||||||
|
rjit_record_exit_stack
|
||||||
],
|
],
|
||||||
types: %w[
|
types: %w[
|
||||||
CALL_DATA
|
CALL_DATA
|
||||||
|
Loading…
x
Reference in New Issue
Block a user