#!/usr/bin/ruby

UNIT = 1000.0
DEBUG = false
RESULT_ROOT = ENV['RESULT_ROOT']

LKP_SRC = ENV['LKP_SRC'] || File.dirname(File.dirname(File.realpath($PROGRAM_NAME)))

require "#{LKP_SRC}/lib/noise"
require "#{LKP_SRC}/lib/log"

time = {}
syscall_nr = {}

sys_nr = {}
sys_nr['total'] = 0
sys_nr['max'] = 0

def parse_process_lines(process_lines, time)
  syscall1 = syscall2 = ''
  syscall_start = syscall_end = 0
  start = true
  find_next = true
  last_line = 'init'

  process_lines.each do |line|
    a = line.split(']')
    b = a[1].split

    if find_next
      if b[3] == '->'
        last_line = line
        next
      else
        find_next = false
        start = true
      end
    end

    if start
      if b[3] == '->'
        log_warn 'not a START of syscall\n' + last_line + line if DEBUG
        find_next = true
        last_line = line
        next
      end
      c = b[2].split('(')
      syscall1 = c[0]
      syscall_start = b[1].to_i
      start = false
    else
      if b[3] != '->'
        log_warn 'not a syscall return:\n' + last_line + line if DEBUG
        find_next = true
        last_line = line
        next
      end
      syscall2 = b[2]
      if syscall2 != syscall1
        log_warn 'not the same syscall\n' + last_line + line if DEBUG
        find_next = true
        last_line = line
        next
      end
      syscall_end = b[1].to_i
      syscall_time = syscall_end - syscall_start
      time[syscall2] = [] if time[syscall2].nil?
      time[syscall2] << syscall_time
      start = true
    end
    last_line = line
  end
end

def parse_syscalls(time)
  process_lines = {}
  lines = if File.exist?("#{RESULT_ROOT}/ftrace.data.xz")
            IO.popen("xzcat #{RESULT_ROOT}/ftrace.data.xz").readlines
          elsif File.exist?("#{RESULT_ROOT}/ftrace.data")
            IO.readlines("#{RESULT_ROOT}/ftrace.data")
          end

  if lines.nil?
    log_error "no ftrace.data or ftrace.data.xz in the #{RESULT_ROOT}"
    return
  end

  lines.each do |line|
    next if line.include?('CPU') || line.include?('#')

    a = line.split
    next if a[4].nil?

    process_name = a[0]
    process_lines[process_name] = [] if process_lines[process_name].nil?
    process_lines[process_name] << line
  end

  process_lines.each do |_process_name, line_array|
    parse_process_lines line_array, time
  end
end

def get_syscall_nr(time, syscall_nr, sys_nr)
  time.each do |syscall, time_array|
    syscall_nr[syscall] = time_array.length
  end

  syscall_nr.each do |_syscall, nr|
    sys_nr['total'] += nr
    sys_nr['max'] = nr if nr > sys_nr['max']
  end

  syscall_nr.sort_by { |_key, val| val }.reverse
end

def show_syscalls_noise(time, syscall_nr, sys_nr)
  a = get_syscall_nr time, syscall_nr, sys_nr
  i = 0
  a.each do |syscall, _nr|
    break if i == 5

    n = Noise.new syscall, time[syscall]
    n.analyse
    n.log
    i += 1
  end
end

parse_syscalls time
exit if time.nil?
show_syscalls_noise time, syscall_nr, sys_nr
