linux_process_memory 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 31ab74b4c6400c74e5b44f8e566b3814f4967aad8f6921c243c9d6cc9c8cec6b
4
+ data.tar.gz: 9fad9bf95d085146dcdb427f09c5a1670e49b65f56c611f726caa2e003ac9d7b
5
+ SHA512:
6
+ metadata.gz: 34e6e1eb617e34c402355a13441585cb0f8c38bf239da87be5f04257815ef9d4ce1342f4c9ab9c1531fd85247a379ab88d1a040e4b37c3e91293930024147b7e
7
+ data.tar.gz: 90e4549cb6d9d3428343d7cdc0d4f0e01193b8b5a0b5df626390816367ceff839fd69f9e91c2f25edcc4e48188344b40a6101fe3431e6d141a685b1cc8133db4
data/CHANGELOG.md ADDED
@@ -0,0 +1,10 @@
1
+ # Changelog
2
+ All notable changes to this project will be documented in this file.
3
+
4
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## 1.0.0
8
+
9
+ ### Added
10
+ - Initial release.
data/MIT-LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2023 Brian Durand
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,108 @@
1
+ # Linux Process Memory Ruby Gem
2
+
3
+ [![Continuous Integration](https://github.com/bdurand/linux_process_memory/actions/workflows/continuous_integration.yml/badge.svg)](https://github.com/bdurand/linux_process_memory/actions/workflows/continuous_integration.yml)
4
+ [![Ruby Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://github.com/testdouble/standard)
5
+
6
+ Ruby gem to get a breakdown of the memory being used by a Linux process. It is specific to Linux and will not work on other operating systems even if they are Linux-like (i.e. MacOS, Windows, FreeBSD, etc.). The breakdown takes into account shared memory and swap memory. It is most useful for monitoring memory usage of processes that use shared memory.
7
+
8
+ If you need similar functionality like this on other platforms, you can use the [get_process_mem gem](https://github.com/zombocom/get_process_mem).
9
+
10
+ ## Usage
11
+
12
+ Pass in a process pid to get a breakdown of the memory being used by that process.
13
+
14
+ ```ruby
15
+ memory = LinuxProcessMemory.new(1234)
16
+ ```
17
+
18
+ If you don't pass in a pid, it will get the memory for the current process.
19
+
20
+ ```ruby
21
+ memory = LinuxProcessMemory.new
22
+ ```
23
+
24
+ The memory breakdown is captured at the time the object is created. To get the memory breakdown at a different time, create a new object.
25
+
26
+ Memory is complicated in Linux and there are many different ways to measure it depending on how you want to count shared memory and swap. This gem provides a few different ways to measure memory usage. The following methods are available:
27
+
28
+ ```ruby
29
+ memory = LinuxProcessMemory.new
30
+ memory.total # => total memory used by the process (resident + swap)
31
+ memory.swap # => swap memory used
32
+ memory.shared # => shared memory used
33
+ memory.rss # => resident set size (i.e. non-swap memory allocated)
34
+ memory.resident # same as rss
35
+ memory.pss # => proportional set size (resident size + shared memory / number of processes)
36
+ memory.proportional # same as pss
37
+ memory.uss # => unique set size (resident memory not shared with other processes)
38
+ memory.unique # same as uss
39
+ memory.referenced # => memory actively referenced by the process (i.e. non-freeable memory)
40
+ ```
41
+
42
+ These measurements tend to be the mose useful ones especially if your processes are using shared memory:
43
+
44
+ - [Resident Set Size](https://en.wikipedia.org/wiki/Resident_set_size)
45
+ - [Proportional Set Size](https://en.wikipedia.org/wiki/Proportional_set_size)
46
+ - [Unique Set Size](https://en.wikipedia.org/wiki/Unique_set_size)
47
+
48
+ Values are returned in bytes, but you can request different units by passing in an optional argument to indicate the unit. Note that requesting a unit other than bytes will return a `Float` instead of an `Integer`.
49
+
50
+ ```ruby
51
+ memory = LinuxProcessMemory.new
52
+ memory.total(:kb) # => total memory used by the process in kilobytes
53
+ memory.total(:mb) # => total memory used by the process in megabytes
54
+ memory.total(:gb) # => total memory used by the process in gigabytes
55
+ ```
56
+
57
+ This gem is specific to Linux. If you try to use it on a non-Linux platform then memory values will always be returned as -1. If you want to check if the gem is supported on your platform, you can use the `supported?` method.
58
+
59
+ ```ruby
60
+ if LinuxProcessMemory.supported?
61
+ memory = LinuxProcessMemory.new
62
+ end
63
+ ```
64
+
65
+ ### Example
66
+
67
+ Here's an example of how you might use this gem to collect memory information on your processes by logging resident memory every minute.
68
+
69
+ ```ruby
70
+ if LinuxProcessMemory.supported?
71
+ logger = Logger.new($stderr)
72
+ Thread.new do
73
+ loop do
74
+ memory = LinuxProcessMemory.new
75
+ logger.info("Proportional memory: #{memory.pss(:mb).round} MB (pid: #{Process.pid})")
76
+ sleep(60)
77
+ end
78
+ end
79
+ end
80
+ ```
81
+
82
+ ## Installation
83
+
84
+ Add this line to your application's Gemfile:
85
+
86
+ ```ruby
87
+ gem "linux_process_memory"
88
+ ```
89
+
90
+ Then execute:
91
+ ```bash
92
+ $ bundle
93
+ ```
94
+
95
+ Or install it yourself as:
96
+ ```bash
97
+ $ gem install linux_process_memory
98
+ ```
99
+
100
+ ## Contributing
101
+
102
+ Open a pull request on [GitHub](https://github.com/bdurand/linux_process_memory).
103
+
104
+ Please use the [standardrb](https://github.com/testdouble/standard) syntax and lint your code with `standardrb --fix` before submitting.
105
+
106
+ ## License
107
+
108
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
@@ -0,0 +1,155 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This class will read the smap files for a process on a Linux system and report
4
+ # the memory usage for that process.
5
+ class LinuxProcessMemory
6
+ VERSION = File.read(File.expand_path("../VERSION", __dir__)).strip.freeze
7
+
8
+ LINUX_MATCHER = /linux/i
9
+ private_constant :LINUX_MATCHER
10
+
11
+ UNIT_CONVERSION = {
12
+ "bytes" => 1,
13
+ "kilobytes" => 1024,
14
+ "megabytes" => 1024 * 1024,
15
+ "gigabytes" => 1024 * 1024 * 1024,
16
+ "kb" => 1024,
17
+ "mb" => 1024 * 1024,
18
+ "gb" => 1024 * 1024 * 1024,
19
+ "k" => 1024,
20
+ "m" => 1024 * 1024,
21
+ "g" => 1024 * 1024 * 1024
22
+ }.freeze
23
+ private_constant :UNIT_CONVERSION
24
+
25
+ attr_reader :pid
26
+
27
+ class << self
28
+ # Returns true if the current platform is Linux.
29
+ #
30
+ # @return [Boolean]
31
+ def supported?
32
+ RUBY_PLATFORM.match?(LINUX_MATCHER)
33
+ end
34
+ end
35
+
36
+ # Create a memory snapshot for the specified process.
37
+ #
38
+ # @param pid [Integer] The process ID to snapshot. Defaults to the current process.
39
+ def initialize(pid = Process.pid)
40
+ @pid = pid
41
+ @stats = (self.class.supported? ? read_smaps : Hash.new(-1))
42
+ end
43
+
44
+ # Returns the total memory usage for the process.
45
+ #
46
+ # @param units [Symbol] The units to return the memory usage in.
47
+ # Valid values are :bytes, :kilobytes, :megabytes, :gigabytes, :kb, :mb, :gb, :k, :m, :g.
48
+ # Defaults to :bytes.
49
+ # @return [Numberic]
50
+ def total(units = :bytes)
51
+ convert_units(@stats[:Rss] + @stats[:Swap], units)
52
+ end
53
+
54
+ # Returns the resident set size for the process.
55
+ #
56
+ # @param units [Symbol] The units to return the memory usage in.
57
+ # Valid values are :bytes, :kilobytes, :megabytes, :gigabytes, :kb, :mb, :gb, :k, :m, :g.
58
+ # Defaults to :bytes.
59
+ # @return [Numberic]
60
+ def rss(units = :bytes)
61
+ convert_units(@stats[:Rss], units)
62
+ end
63
+
64
+ alias_method :resident, :rss
65
+
66
+ # Returns the proportional set size for the process.
67
+ #
68
+ # @param units [Symbol] The units to return the memory usage in.
69
+ # Valid values are :bytes, :kilobytes, :megabytes, :gigabytes, :kb, :mb, :gb, :k, :m, :g.
70
+ # Defaults to :bytes.
71
+ # @return [Numberic]
72
+ def pss(units = :bytes)
73
+ convert_units(@stats[:Pss], units)
74
+ end
75
+
76
+ alias_method :proportional, :pss
77
+
78
+ # Returns the uniq set size for the process.
79
+ #
80
+ # @param units [Symbol] The units to return the memory usage in.
81
+ # Valid values are :bytes, :kilobytes, :megabytes, :gigabytes, :kb, :mb, :gb, :k, :m, :g.
82
+ # Defaults to :bytes.
83
+ # @return [Numberic]
84
+ def uss(units = :bytes)
85
+ convert_units(@stats[:Private_Clean] + @stats[:Private_Dirty], units)
86
+ end
87
+
88
+ alias_method :unique, :uss
89
+
90
+ # Returns the swap used by the process.
91
+ #
92
+ # @param units [Symbol] The units to return the memory usage in.
93
+ # Valid values are :bytes, :kilobytes, :megabytes, :gigabytes, :kb, :mb, :gb, :k, :m, :g.
94
+ # Defaults to :bytes.
95
+ # @return [Numberic]
96
+ def swap(units = :bytes)
97
+ convert_units(@stats[:Swap], units)
98
+ end
99
+
100
+ # Returns the shared memory used by the process.
101
+ #
102
+ # @param units [Symbol] The units to return the memory usage in.
103
+ # Valid values are :bytes, :kilobytes, :megabytes, :gigabytes, :kb, :mb, :gb, :k, :m, :g.
104
+ # Defaults to :bytes.
105
+ # @return [Numberic]
106
+ def shared(units = :bytes)
107
+ convert_units(@stats[:Shared_Clean] + @stats[:Shared_Dirty], units)
108
+ end
109
+
110
+ # Returns the referenced memory size for the process (i.e. memory that is actively being used)
111
+ # that cannot be reclaimed.
112
+ #
113
+ # @param units [Symbol] The units to return the memory usage in.
114
+ # Valid values are :bytes, :kilobytes, :megabytes, :gigabytes, :kb, :mb, :gb, :k, :m, :g.
115
+ # Defaults to :bytes.
116
+ # @return [Numberic]
117
+ def referenced(units = :bytes)
118
+ convert_units(@stats[:Referenced], units)
119
+ end
120
+
121
+ private
122
+
123
+ def read_smaps
124
+ stats = Hash.new(0)
125
+ return stats unless File.exist?(smap_rollup_file)
126
+
127
+ data = File.read(smap_rollup_file).split("\n")
128
+ data.shift # remove header
129
+ data.each do |line|
130
+ key, value, unit = line.split
131
+ key = key.chomp(":").to_sym
132
+
133
+ multiplier = UNIT_CONVERSION.fetch(unit.to_s.downcase, 1)
134
+ numeric_value = (value.to_f * multiplier).round
135
+ stats[key] += numeric_value if numeric_value > 0
136
+ end
137
+
138
+ stats
139
+ end
140
+
141
+ def convert_units(value, units)
142
+ return -1 if value < 0
143
+
144
+ divisor = UNIT_CONVERSION[units.to_s.downcase]
145
+ raise ArgumentError.new("Unknown units: #{units}") unless divisor
146
+
147
+ return value if divisor == 1
148
+
149
+ value.to_f / divisor
150
+ end
151
+
152
+ def smap_rollup_file
153
+ "/proc/#{pid}/smaps_rollup"
154
+ end
155
+ end
@@ -0,0 +1,35 @@
1
+ Gem::Specification.new do |spec|
2
+ spec.name = "linux_process_memory"
3
+ spec.version = File.read(File.expand_path("../VERSION", __FILE__)).strip
4
+ spec.authors = ["Brian Durand"]
5
+ spec.email = ["bbdurand@gmail.com"]
6
+
7
+ spec.summary = "Get a breakdown of the memory being used by a Linux process including resident, shared, private, and swap memory."
8
+
9
+ spec.homepage = "https://github.com/bdurand/linux_process_memory"
10
+ spec.license = "MIT"
11
+
12
+ # Specify which files should be added to the gem when it is released.
13
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
14
+ ignore_files = %w[
15
+ .
16
+ Appraisals
17
+ Gemfile
18
+ Gemfile.lock
19
+ Rakefile
20
+ config.ru
21
+ assets/
22
+ bin/
23
+ gemfiles/
24
+ spec/
25
+ ]
26
+ spec.files = Dir.chdir(File.expand_path("..", __FILE__)) do
27
+ `git ls-files -z`.split("\x0").reject { |f| ignore_files.any? { |path| f.start_with?(path) } }
28
+ end
29
+
30
+ spec.require_paths = ["lib"]
31
+
32
+ spec.required_ruby_version = ">= 2.5"
33
+
34
+ spec.add_development_dependency "bundler"
35
+ end
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: linux_process_memory
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Brian Durand
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-08-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description:
28
+ email:
29
+ - bbdurand@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - CHANGELOG.md
35
+ - MIT-LICENSE.txt
36
+ - README.md
37
+ - VERSION
38
+ - lib/linux_process_memory.rb
39
+ - linux_process_memory.gemspec
40
+ homepage: https://github.com/bdurand/linux_process_memory
41
+ licenses:
42
+ - MIT
43
+ metadata: {}
44
+ post_install_message:
45
+ rdoc_options: []
46
+ require_paths:
47
+ - lib
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: '2.5'
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ requirements: []
59
+ rubygems_version: 3.2.22
60
+ signing_key:
61
+ specification_version: 4
62
+ summary: Get a breakdown of the memory being used by a Linux process including resident,
63
+ shared, private, and swap memory.
64
+ test_files: []