debian-mirror-gitlab/lib/gitlab/metrics/system.rb

146 lines
4.8 KiB
Ruby
Raw Normal View History

2019-02-15 15:39:39 +05:30
# frozen_string_literal: true
module Gitlab
module Metrics
# 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.
module System
2022-04-04 11:22:00 +05:30
extend self
PROC_STAT_PATH = '/proc/self/stat'
2022-10-11 01:57:18 +05:30
PROC_STATUS_PATH = '/proc/%s/status'
PROC_SMAPS_ROLLUP_PATH = '/proc/%s/smaps_rollup'
2020-05-24 23:13:21 +05:30
PROC_LIMITS_PATH = '/proc/self/limits'
PROC_FD_GLOB = '/proc/self/fd/*'
PRIVATE_PAGES_PATTERN = /^(Private_Clean|Private_Dirty|Private_Hugetlb):\s+(?<value>\d+)/.freeze
PSS_PATTERN = /^Pss:\s+(?<value>\d+)/.freeze
RSS_PATTERN = /VmRSS:\s+(?<value>\d+)/.freeze
MAX_OPEN_FILES_PATTERN = /Max open files\s*(?<value>\d+)/.freeze
2022-04-04 11:22:00 +05:30
def summary
2021-03-08 18:12:59 +05:30
proportional_mem = memory_usage_uss_pss
{
version: RUBY_DESCRIPTION,
gc_stat: GC.stat,
memory_rss: memory_usage_rss,
memory_uss: proportional_mem[:uss],
memory_pss: proportional_mem[:pss],
time_cputime: cpu_time,
time_realtime: real_time,
time_monotonic: monotonic_time
}
end
2022-10-11 01:57:18 +05:30
# Returns the given process' RSS (resident set size) in bytes.
def memory_usage_rss(pid: 'self')
sum_matches(PROC_STATUS_PATH % pid, rss: RSS_PATTERN)[:rss].kilobytes
2020-05-24 23:13:21 +05:30
end
2019-09-04 21:01:54 +05:30
2022-10-11 01:57:18 +05:30
# Returns the given process' USS/PSS (unique/proportional set size) in bytes.
def memory_usage_uss_pss(pid: 'self')
sum_matches(PROC_SMAPS_ROLLUP_PATH % pid, uss: PRIVATE_PAGES_PATTERN, pss: PSS_PATTERN)
2020-05-24 23:13:21 +05:30
.transform_values(&:kilobytes)
end
2022-04-04 11:22:00 +05:30
def file_descriptor_count
2020-05-24 23:13:21 +05:30
Dir.glob(PROC_FD_GLOB).length
end
2019-09-04 21:01:54 +05:30
2022-04-04 11:22:00 +05:30
def max_open_file_descriptors
2020-05-24 23:13:21 +05:30
sum_matches(PROC_LIMITS_PATH, max_fds: MAX_OPEN_FILES_PATTERN)[:max_fds]
end
2016-06-02 11:05:42 +05:30
2022-04-04 11:22:00 +05:30
def cpu_time
2020-05-24 23:13:21 +05:30
Process.clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID, :float_second)
2016-06-02 11:05:42 +05:30
end
2016-08-24 12:49:21 +05:30
# Returns the current real time in a given precision.
#
2018-03-17 18:26:18 +05:30
# Returns the time as a Float for precision = :float_second.
2022-04-04 11:22:00 +05:30
def real_time(precision = :float_second)
2016-09-13 17:45:13 +05:30
Process.clock_gettime(Process::CLOCK_REALTIME, precision)
2016-08-24 12:49:21 +05:30
end
2018-03-17 18:26:18 +05:30
# Returns the current monotonic clock time as seconds with microseconds precision.
2016-08-24 12:49:21 +05:30
#
# Returns the time as a Float.
2022-04-04 11:22:00 +05:30
def monotonic_time
2018-03-17 18:26:18 +05:30
Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_second)
2016-08-24 12:49:21 +05:30
end
2019-12-21 20:55:43 +05:30
2022-04-04 11:22:00 +05:30
def thread_cpu_time
2019-12-21 20:55:43 +05:30
# 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
2022-04-04 11:22:00 +05:30
def thread_cpu_duration(start_time)
2019-12-21 20:55:43 +05:30
end_time = thread_cpu_time
return unless start_time && end_time
end_time - start_time
end
2020-05-24 23:13:21 +05:30
2022-04-04 11:22:00 +05:30
# Returns the total time the current process has been running in seconds.
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
private
2020-05-24 23:13:21 +05:30
# Given a path to a file in /proc and a hash of (metric, pattern) pairs,
# sums up all values found for those patterns under the respective metric.
2022-04-04 11:22:00 +05:30
def sum_matches(proc_file, **patterns)
2020-05-24 23:13:21 +05:30
results = patterns.transform_values { 0 }
2022-04-04 11:22:00 +05:30
safe_yield_procfile(proc_file) do |io|
io.each_line do |line|
2020-05-24 23:13:21 +05:30
patterns.each do |metric, pattern|
match = line.match(pattern)
value = match&.named_captures&.fetch('value', 0)
results[metric] += value.to_i
end
end
end
results
end
2022-04-04 11:22:00 +05:30
def proc_stat_entries
safe_yield_procfile(PROC_STAT_PATH) do |io|
io.read.split(' ')
end || []
end
def safe_yield_procfile(path, &block)
File.open(path, &block)
rescue Errno::ENOENT
# This means the procfile we're reading from did not exist;
# most likely we're on Darwin.
end
# Equivalent to reading /proc/uptime on Linux 2.6+.
#
# Returns 0 if not supported, e.g. on Darwin.
def uptime
Process.clock_gettime(Process::CLOCK_BOOTTIME)
rescue NameError
0
end
end
end
end