module RubyVM::RJIT

Constants

CFP
C_ARGS

SystemV x64 calling convention

C_RET
CantCompile
Default
EC

Callee-saved registers TODO: support using r12/r13 here

EndBlock
GC_REFS

Mark objects in this Array during GC

KeepCompiling

Compilation status

MAX_LOCAL_TYPES

Maximum number of local variable types we keep track of

MAX_TEMP_TYPES

Maximum number of temp value types we keep track of

MAX_VERSIONS

Maximum number of versions per block 1 means always create generic versions

MapToLocal
MapToSelf
MapToStack

Potential mapping of a value on the temporary stack to self, a local variable, or constant so that we can track its type

Next0

Branch shapes

Next1
Qfalse
Qnil
Qtrue

Ruby constants

Qundef
QwordPtr

64-bit memory access

SP
SelfOpnd

Operand to a YARV bytecode instruction

StackOpnd
Type

Represent the type of a value (local/stack/self) in RJIT

Public Class Methods

runtime_stats() click to toggle source
# File ruby_vm/rjit/stats.rb, line 3
def self.runtime_stats
  stats = {}

  # Insn exits
  INSNS.each_value do |insn|
    exits = C.rjit_insn_exits[insn.bin]
    if exits > 0
      stats[:"exit_#{insn.name}"] = exits
    end
  end

  # Runtime stats
  C.rb_rjit_runtime_counters.members.each do |member|
    stats[member] = C.rb_rjit_counters.public_send(member)
  end

  # Other stats are calculated here
  stats[:side_exit_count] = stats.select { |name, _count| name.start_with?('exit_') }.sum(&:last)
  if stats[:vm_insns_count] > 0
    retired_in_rjit = stats[:rjit_insns_count] - stats[:side_exit_count]
    stats[:total_insns_count] = retired_in_rjit + stats[:vm_insns_count]
    stats[:ratio_in_rjit] = 100.0 * retired_in_rjit / stats[:total_insns_count]
  else
    stats.delete(:vm_insns_count)
  end

  stats
end

Private Class Methods

dump_trace_exits() click to toggle source

–yjit-trace-exits at_exit

# File ruby_vm/rjit/stats.rb, line 107
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
exit_traces() click to toggle source

Convert rb_rjit_raw_samples and rb_rjit_line_samples into a StackProf format.

# File ruby_vm/rjit/stats.rb, line 114
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
format_number(pad, number) click to toggle source

Format large numbers with comma separators for readability

# File ruby_vm/rjit/stats.rb, line 99
def format_number(pad, number)
  integer, decimal = number.to_s.split('.')
  d_groups = integer.chars.reverse.each_slice(3)
  with_commas = d_groups.map(&:join).join(',').reverse
  [with_commas, decimal].compact.join('.').rjust(pad, ' ')
end
print_counters(stats, prefix:, prompt:) click to toggle source
print_exit_counts(stats, how_many: 20, padding: 2) click to toggle source
print_stats() click to toggle source

–yjit-stats at_exit