diff options
Diffstat (limited to 'lib/gitlab/metrics/system.rb')
-rw-r--r-- | lib/gitlab/metrics/system.rb | 69 |
1 files changed, 53 insertions, 16 deletions
diff --git a/lib/gitlab/metrics/system.rb b/lib/gitlab/metrics/system.rb index 9bbcd1e056c..e646846face 100644 --- a/lib/gitlab/metrics/system.rb +++ b/lib/gitlab/metrics/system.rb @@ -7,6 +7,9 @@ module Gitlab # This module relies on the /proc filesystem being available. If /proc is # not available the methods of this module will be stubbed. module System + extend self + + PROC_STAT_PATH = '/proc/self/stat' PROC_STATUS_PATH = '/proc/self/status' PROC_SMAPS_ROLLUP_PATH = '/proc/self/smaps_rollup' PROC_LIMITS_PATH = '/proc/self/limits' @@ -17,7 +20,7 @@ module Gitlab RSS_PATTERN = /VmRSS:\s+(?<value>\d+)/.freeze MAX_OPEN_FILES_PATTERN = /Max open files\s*(?<value>\d+)/.freeze - def self.summary + def summary proportional_mem = memory_usage_uss_pss { version: RUBY_DESCRIPTION, @@ -32,43 +35,43 @@ module Gitlab end # Returns the current process' RSS (resident set size) in bytes. - def self.memory_usage_rss + def memory_usage_rss sum_matches(PROC_STATUS_PATH, rss: RSS_PATTERN)[:rss].kilobytes end # Returns the current process' USS/PSS (unique/proportional set size) in bytes. - def self.memory_usage_uss_pss + def memory_usage_uss_pss sum_matches(PROC_SMAPS_ROLLUP_PATH, uss: PRIVATE_PAGES_PATTERN, pss: PSS_PATTERN) .transform_values(&:kilobytes) end - def self.file_descriptor_count + def file_descriptor_count Dir.glob(PROC_FD_GLOB).length end - def self.max_open_file_descriptors + def max_open_file_descriptors sum_matches(PROC_LIMITS_PATH, max_fds: MAX_OPEN_FILES_PATTERN)[:max_fds] end - def self.cpu_time + def cpu_time Process.clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID, :float_second) end # Returns the current real time in a given precision. # # Returns the time as a Float for precision = :float_second. - def self.real_time(precision = :float_second) + def real_time(precision = :float_second) Process.clock_gettime(Process::CLOCK_REALTIME, precision) end # Returns the current monotonic clock time as seconds with microseconds precision. # # Returns the time as a Float. - def self.monotonic_time + def monotonic_time Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_second) end - def self.thread_cpu_time + 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) @@ -76,33 +79,67 @@ module Gitlab Process.clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID, :float_second) end - def self.thread_cpu_duration(start_time) + def thread_cpu_duration(start_time) end_time = thread_cpu_time return unless start_time && end_time end_time - start_time end + # 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 + # 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. - def self.sum_matches(proc_file, **patterns) + def sum_matches(proc_file, **patterns) results = patterns.transform_values { 0 } - begin - File.foreach(proc_file) do |line| + safe_yield_procfile(proc_file) do |io| + io.each_line do |line| patterns.each do |metric, pattern| match = line.match(pattern) value = match&.named_captures&.fetch('value', 0) results[metric] += value.to_i end end - rescue Errno::ENOENT - # This means the procfile we're reading from did not exist; - # this is safe to ignore, since we initialize each metric to 0 end results end + + 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 |