linux_process_memory 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +10 -0
- data/MIT-LICENSE.txt +20 -0
- data/README.md +108 -0
- data/VERSION +1 -0
- data/lib/linux_process_memory.rb +155 -0
- data/linux_process_memory.gemspec +35 -0
- metadata +64 -0
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
|
+
[](https://github.com/bdurand/linux_process_memory/actions/workflows/continuous_integration.yml)
|
4
|
+
[](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: []
|