Module: Gitlab::Metrics::System

Extended by:
System
Included in:
System
Defined in:
lib/gitlab/metrics/system.rb

Overview

Module for gathering system/process statistics such as the memory usage.

This module relies on the /proc filesystem being available. If /proc is not available the methods of this module will be stubbed.

Constant Summary collapse

PROC_STAT_PATH =
'/proc/self/stat'
PROC_STATUS_PATH =
'/proc/%s/status'
PROC_SMAPS_ROLLUP_PATH =
'/proc/%s/smaps_rollup'
PROC_LIMITS_PATH =
'/proc/self/limits'
PROC_FD_GLOB =
'/proc/self/fd/*'
PROC_MEM_INFO =
'/proc/meminfo'
PRIVATE_PAGES_PATTERN =
/^(Private_Clean|Private_Dirty|Private_Hugetlb):\s+(?<value>\d+)/
PSS_PATTERN =
/^Pss:\s+(?<value>\d+)/
RSS_TOTAL_PATTERN =
/^VmRSS:\s+(?<value>\d+)/
RSS_ANON_PATTERN =
/^RssAnon:\s+(?<value>\d+)/
RSS_FILE_PATTERN =
/^RssFile:\s+(?<value>\d+)/
MAX_OPEN_FILES_PATTERN =
/Max open files\s*(?<value>\d+)/
MEM_TOTAL_PATTERN =
/^MemTotal:\s+(?<value>\d+) (.+)/

Instance Method Summary collapse

Instance Method Details

#cpu_timeObject



78
79
80
# File 'lib/gitlab/metrics/system.rb', line 78

def cpu_time
  Process.clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID, :float_second)
end

#file_descriptor_countObject



70
71
72
# File 'lib/gitlab/metrics/system.rb', line 70

def file_descriptor_count
  Dir.glob(PROC_FD_GLOB).length
end

#max_open_file_descriptorsObject



74
75
76
# File 'lib/gitlab/metrics/system.rb', line 74

def max_open_file_descriptors
  sum_matches(PROC_LIMITS_PATH, max_fds: MAX_OPEN_FILES_PATTERN)[:max_fds]
end

#memory_totalObject



66
67
68
# File 'lib/gitlab/metrics/system.rb', line 66

def memory_total
  sum_matches(PROC_MEM_INFO, memory_total: MEM_TOTAL_PATTERN)[:memory_total].kilobytes
end

#memory_usage_rss(pid: 'self') ⇒ Object

Returns the given process’ RSS (resident set size) in bytes.



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/gitlab/metrics/system.rb', line 42

def memory_usage_rss(pid: 'self')
  results = { total: 0, anon: 0, file: 0 }

  safe_yield_procfile(PROC_STATUS_PATH % pid) do |io|
    io.each_line do |line|
      if (value = parse_metric_value(line, RSS_TOTAL_PATTERN)) > 0
        results[:total] = value.kilobytes
      elsif (value = parse_metric_value(line, RSS_ANON_PATTERN)) > 0
        results[:anon] = value.kilobytes
      elsif (value = parse_metric_value(line, RSS_FILE_PATTERN)) > 0
        results[:file] = value.kilobytes
      end
    end
  end

  results
end

#memory_usage_uss_pss(pid: 'self') ⇒ Object

Returns the given process’ USS/PSS (unique/proportional set size) in bytes.



61
62
63
64
# File 'lib/gitlab/metrics/system.rb', line 61

def memory_usage_uss_pss(pid: 'self')
  sum_matches(PROC_SMAPS_ROLLUP_PATH % pid, uss: PRIVATE_PAGES_PATTERN, pss: PSS_PATTERN)
    .transform_values(&:kilobytes)
end

#monotonic_timeObject

Returns the current monotonic clock time as seconds with microseconds precision.

Returns the time as a Float.



92
93
94
# File 'lib/gitlab/metrics/system.rb', line 92

def monotonic_time
  Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_second)
end

#process_runtime_elapsed_secondsObject

Returns the total time the current process has been running in seconds.



112
113
114
115
116
117
118
119
120
121
122
# File 'lib/gitlab/metrics/system.rb', line 112

def process_runtime_elapsed_seconds
  # Entry 22 (1-indexed) contains the process `starttime`, see:
  # https://man7.org/linux/man-pages/man5/proc.5.html
  #
  # This value is a fixed timestamp in clock ticks.
  # To obtain an elapsed time in seconds, we divide by the number
  # of ticks per second and subtract from the system uptime.
  start_time_ticks = proc_stat_entries[21].to_f
  clock_ticks_per_second = Etc.sysconf(Etc::SC_CLK_TCK)
  uptime - (start_time_ticks / clock_ticks_per_second)
end

#real_time(precision = :float_second) ⇒ Object

Returns the current real time in a given precision.

Returns the time as a Float for precision = :float_second.



85
86
87
# File 'lib/gitlab/metrics/system.rb', line 85

def real_time(precision = :float_second)
  Process.clock_gettime(Process::CLOCK_REALTIME, precision)
end

#summaryObject



27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/gitlab/metrics/system.rb', line 27

def summary
  proportional_mem = memory_usage_uss_pss
  {
    version: RUBY_DESCRIPTION,
    gc_stat: GC.stat,
    memory_rss: memory_usage_rss[:total],
    memory_uss: proportional_mem[:uss],
    memory_pss: proportional_mem[:pss],
    time_cputime: cpu_time,
    time_realtime: real_time,
    time_monotonic: monotonic_time
  }
end

#thread_cpu_duration(start_time) ⇒ Object



104
105
106
107
108
109
# File 'lib/gitlab/metrics/system.rb', line 104

def thread_cpu_duration(start_time)
  end_time = thread_cpu_time
  return unless start_time && end_time

  end_time - start_time
end

#thread_cpu_timeObject



96
97
98
99
100
101
102
# File 'lib/gitlab/metrics/system.rb', line 96

def thread_cpu_time
  # Not all OS kernels are supporting `Process::CLOCK_THREAD_CPUTIME_ID`
  # Refer: https://gitlab.com/gitlab-org/gitlab/issues/30567#note_221765627
  return unless defined?(Process::CLOCK_THREAD_CPUTIME_ID)

  Process.clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID, :float_second)
end