process-metrics 0.3.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +2 -2
- data/bin/process-metrics +1 -1
- data/lib/process/metrics/command/summary.rb +40 -31
- data/lib/process/metrics/command/top.rb +8 -8
- data/lib/process/metrics/command.rb +2 -2
- data/lib/process/metrics/general.rb +35 -21
- data/lib/process/metrics/memory/darwin.rb +110 -0
- data/lib/process/metrics/memory/linux.rb +111 -0
- data/lib/process/metrics/memory.rb +17 -81
- data/lib/process/metrics/version.rb +1 -1
- data/license.md +1 -1
- data/readme.md +3 -3
- data.tar.gz.sig +0 -0
- metadata +12 -15
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cbfff6ee5c7b2f09e1872bfaf86b520d999c94295de8d2328140ffeb84baa228
|
4
|
+
data.tar.gz: 2318ca3ac6a699863015b0f4ac32d22ee8845466f7046feee0155e89b9c5c5a8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b4a69c2cc8a4055fcb9cd3612459b4e1a1164b8a4ac4095dafc8e7b51126915c79673b6023bc09f87357d69ac0aaf71e0caae9c2d3dd6a4fd3a7f8482d368b00
|
7
|
+
data.tar.gz: 17363223b2c6858e5f2e0241267e6dbaf6e97f58631483ef95af6fd7ad349d59915a8e486a27011a259c64f290f4da8ced1ea96e47bb3d0eb193c877ffdf2591
|
checksums.yaml.gz.sig
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
�) ��w�RC���*!�i&c��ը�+#�]���?�-Z,:T&����~`
|
2
|
+
�fõ9�mZߤx5$Юs��}��)�C̙}�6�X}�;q�u�����z�KB�<8��|��Ѻ�wg�"�0>�6��[ZRѦ�w?Al��O�i��n|�-�{� � �����o/�t2�1�vu�\��3�sgb��WcL/�5yoF��h+e�/T�ڧ3|;IVEM��ۏ�r�Q�T�|�4�j_�:��=�����9�].�EN�Q�g��u�4�Ѵ�9\��_���
|
data/bin/process-metrics
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2020-
|
4
|
+
# Copyright, 2020-2025, by Samuel Williams.
|
5
5
|
|
6
|
-
require
|
6
|
+
require "samovar"
|
7
7
|
|
8
|
-
require_relative
|
8
|
+
require_relative "../general"
|
9
9
|
|
10
|
-
require
|
10
|
+
require "console/terminal"
|
11
11
|
|
12
12
|
module Process
|
13
13
|
module Metrics
|
@@ -42,10 +42,10 @@ module Process
|
|
42
42
|
self.description = "Display a summary of memory usage statistics."
|
43
43
|
|
44
44
|
options do
|
45
|
-
option
|
46
|
-
option
|
45
|
+
option "--pid <integer>", "Report on a single process id.", type: Integer, required: true
|
46
|
+
option "-p/--ppid <integer>", "Report on all children of this process id.", type: Integer, required: true
|
47
47
|
|
48
|
-
option
|
48
|
+
option "--total-memory <integer>", "Set the total memory relative to the usage (MiB).", type: Integer
|
49
49
|
end
|
50
50
|
|
51
51
|
def terminal
|
@@ -89,18 +89,26 @@ module Process
|
|
89
89
|
return "#{value.round(unit)}#{units[unit]}"
|
90
90
|
end
|
91
91
|
|
92
|
-
def
|
93
|
-
if value > (
|
92
|
+
def format_memory(value, total, terminal)
|
93
|
+
if value > (total * 0.8)
|
94
94
|
intensity = :high
|
95
|
-
elsif value > (
|
95
|
+
elsif value > (total * 0.5)
|
96
96
|
intensity = :medium
|
97
97
|
else
|
98
98
|
intensity = :low
|
99
99
|
end
|
100
100
|
|
101
|
-
formatted = (format_size(value) +
|
101
|
+
formatted = (format_size(value) + " ").rjust(10)
|
102
102
|
|
103
|
-
terminal.print(formatted, intensity, "[", Bar.format(value /
|
103
|
+
terminal.print(formatted, intensity, "[", Bar.format(value / total.to_f, 60), "]", :reset)
|
104
|
+
end
|
105
|
+
|
106
|
+
def total_memory
|
107
|
+
if total_memory = @options[:total_memory]
|
108
|
+
return total_memory * 1024
|
109
|
+
else
|
110
|
+
return Process::Metrics::Memory.total_size
|
111
|
+
end
|
104
112
|
end
|
105
113
|
|
106
114
|
def call
|
@@ -108,9 +116,10 @@ module Process
|
|
108
116
|
|
109
117
|
summary = Process::Metrics::General.capture(pid: @options[:pid], ppid: @options[:ppid])
|
110
118
|
|
111
|
-
|
112
|
-
|
113
|
-
|
119
|
+
format_memory = self.method(:format_memory).curry
|
120
|
+
shared_memory = 0
|
121
|
+
private_memory = 0
|
122
|
+
total_memory = self.total_memory
|
114
123
|
|
115
124
|
proportional = true
|
116
125
|
|
@@ -122,25 +131,25 @@ module Process
|
|
122
131
|
terminal.print_line
|
123
132
|
|
124
133
|
if memory = general.memory
|
125
|
-
|
126
|
-
|
134
|
+
shared_memory += memory.proportional_size
|
135
|
+
private_memory += memory.unique_size
|
127
136
|
|
128
137
|
terminal.print_line(
|
129
|
-
:key, "Memory
|
130
|
-
|
138
|
+
:key, "Memory: ".rjust(20), :reset,
|
139
|
+
format_memory[memory.proportional_size, total_memory]
|
131
140
|
)
|
132
141
|
|
133
142
|
terminal.print_line(
|
134
|
-
:key, "Private
|
135
|
-
|
143
|
+
:key, "Private Memory: ".rjust(20), :reset,
|
144
|
+
format_memory[memory.unique_size, total_memory]
|
136
145
|
)
|
137
146
|
else
|
138
|
-
|
147
|
+
shared_memory += general.resident_size
|
139
148
|
proportional = false
|
140
149
|
|
141
150
|
terminal.print_line(
|
142
|
-
:key, "Memory
|
143
|
-
|
151
|
+
:key, "Memory: ".rjust(20), :reset,
|
152
|
+
format_memory[general.resident_size, total_memory]
|
144
153
|
)
|
145
154
|
end
|
146
155
|
end
|
@@ -149,24 +158,24 @@ module Process
|
|
149
158
|
|
150
159
|
if proportional
|
151
160
|
terminal.print_line(
|
152
|
-
:key, "Memory
|
153
|
-
|
161
|
+
:key, "Memory: ".rjust(20), :reset,
|
162
|
+
format_memory[shared_memory, total_memory]
|
154
163
|
)
|
155
164
|
|
156
165
|
terminal.print_line(
|
157
|
-
:key, "Memory
|
158
|
-
|
166
|
+
:key, "Private Memory: ".rjust(20), :reset,
|
167
|
+
format_memory[private_memory, total_memory]
|
159
168
|
)
|
160
169
|
else
|
161
170
|
terminal.print_line(
|
162
|
-
:key, "Memory
|
163
|
-
|
171
|
+
:key, "Memory: ".rjust(20), :reset,
|
172
|
+
format_memory[memory, total_memory]
|
164
173
|
)
|
165
174
|
end
|
166
175
|
|
167
176
|
terminal.print_line(
|
168
177
|
:key, "Memory (Total): ".rjust(20), :reset,
|
169
|
-
|
178
|
+
format_memory[shared_memory + private_memory, total_memory]
|
170
179
|
)
|
171
180
|
end
|
172
181
|
end
|
@@ -1,12 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2020-
|
4
|
+
# Copyright, 2020-2025, by Samuel Williams.
|
5
5
|
|
6
|
-
require
|
6
|
+
require "samovar"
|
7
7
|
|
8
|
-
require_relative
|
9
|
-
require_relative
|
8
|
+
require_relative "summary"
|
9
|
+
require_relative "../version"
|
10
10
|
|
11
11
|
module Process
|
12
12
|
module Metrics
|
@@ -15,13 +15,13 @@ module Process
|
|
15
15
|
self.description = "Collect memory usage statistics."
|
16
16
|
|
17
17
|
options do
|
18
|
-
option
|
19
|
-
option
|
18
|
+
option "-h/--help", "Print out help information."
|
19
|
+
option "-v/--version", "Print out the application version."
|
20
20
|
end
|
21
21
|
|
22
22
|
nested :command, {
|
23
|
-
|
24
|
-
}, default:
|
23
|
+
"summary" => Summary,
|
24
|
+
}, default: "summary"
|
25
25
|
|
26
26
|
def call
|
27
27
|
if @options[:version]
|
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2020-
|
4
|
+
# Copyright, 2020-2025, by Samuel Williams.
|
5
5
|
|
6
|
-
require_relative
|
6
|
+
require_relative "command/top"
|
7
7
|
|
8
8
|
module Process
|
9
9
|
module Metrics
|
@@ -1,21 +1,38 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2019-
|
4
|
+
# Copyright, 2019-2025, by Samuel Williams.
|
5
5
|
|
6
|
-
require_relative
|
7
|
-
require
|
8
|
-
require
|
6
|
+
require_relative "memory"
|
7
|
+
require "set"
|
8
|
+
require "json"
|
9
9
|
|
10
10
|
module Process
|
11
11
|
module Metrics
|
12
12
|
PS = "ps"
|
13
13
|
|
14
|
+
DURATION = /\A
|
15
|
+
(?:(?<days>\d+)-)? # Optional days (e.g., '2-')
|
16
|
+
(?:(?<hours>\d+):)? # Optional hours (e.g., '1:')
|
17
|
+
(?<minutes>\d{1,2}): # Minutes (always present, 1 or 2 digits)
|
18
|
+
(?<seconds>\d{2}) # Seconds (exactly 2 digits)
|
19
|
+
(?:\.(?<fraction>\d{1,2}))? # Optional fraction of a second (e.g., '.27')
|
20
|
+
\z/x
|
21
|
+
|
22
|
+
|
14
23
|
# Parse a duration string into seconds.
|
15
24
|
# According to the linux manual page specifications.
|
16
25
|
def self.duration(value)
|
17
|
-
if
|
18
|
-
|
26
|
+
if match = DURATION.match(value)
|
27
|
+
days = match[:days].to_i
|
28
|
+
hours = match[:hours].to_i
|
29
|
+
minutes = match[:minutes].to_i
|
30
|
+
seconds = match[:seconds].to_i
|
31
|
+
fraction = match[:fraction].to_i
|
32
|
+
|
33
|
+
return days * 24 * 60 * 60 + hours * 60 * 60 + minutes * 60 + seconds + fraction / 100.0
|
34
|
+
else
|
35
|
+
return 0.0
|
19
36
|
end
|
20
37
|
end
|
21
38
|
|
@@ -25,10 +42,10 @@ module Process
|
|
25
42
|
ppid: ->(value){value.to_i}, # Parent Process ID
|
26
43
|
pgid: ->(value){value.to_i}, # Process Group ID
|
27
44
|
pcpu: ->(value){value.to_f}, # Percentage CPU
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
etime: self.method(:duration), # Elapsed Time
|
45
|
+
vsz: ->(value){value.to_i}, # Virtual Size (KiB)
|
46
|
+
rss: ->(value){value.to_i}, # Resident Size (KiB)
|
47
|
+
time: self.method(:duration), # CPU Time (seconds)
|
48
|
+
etime: self.method(:duration), # Elapsed Time (seconds)
|
32
49
|
command: ->(value){value}, # Command (name of the process)
|
33
50
|
}
|
34
51
|
|
@@ -56,7 +73,7 @@ module Process
|
|
56
73
|
as_json.to_json(*arguments)
|
57
74
|
end
|
58
75
|
|
59
|
-
# The
|
76
|
+
# The total size of the process in memory, in kilobytes.
|
60
77
|
def total_size
|
61
78
|
if memory = self.memory
|
62
79
|
memory.proportional_size
|
@@ -96,8 +113,10 @@ module Process
|
|
96
113
|
end
|
97
114
|
|
98
115
|
def self.capture_memory(processes)
|
116
|
+
count = processes.size
|
117
|
+
|
99
118
|
processes.each do |pid, process|
|
100
|
-
process.memory = Memory.capture(
|
119
|
+
process.memory = Memory.capture(pid, count: count)
|
101
120
|
end
|
102
121
|
end
|
103
122
|
|
@@ -105,18 +124,18 @@ module Process
|
|
105
124
|
#
|
106
125
|
# @parameter pid [Integer] The process ID to capture.
|
107
126
|
# @parameter ppid [Integer] The parent process ID to capture.
|
108
|
-
def self.capture(pid: nil, ppid: nil, ps: PS)
|
127
|
+
def self.capture(pid: nil, ppid: nil, ps: PS, memory: Memory.supported?)
|
109
128
|
input, output = IO.pipe
|
110
129
|
|
111
130
|
arguments = [ps]
|
112
131
|
|
113
132
|
if pid && ppid.nil?
|
114
|
-
arguments.push("-p", Array(pid).join(
|
133
|
+
arguments.push("-p", Array(pid).join(","))
|
115
134
|
else
|
116
135
|
arguments.push("ax")
|
117
136
|
end
|
118
137
|
|
119
|
-
arguments.push("-o", FIELDS.keys.join(
|
138
|
+
arguments.push("-o", FIELDS.keys.join(","))
|
120
139
|
|
121
140
|
ps_pid = Process.spawn(*arguments, out: output, pgroup: true)
|
122
141
|
|
@@ -130,7 +149,6 @@ module Process
|
|
130
149
|
record = FIELDS.
|
131
150
|
zip(line.split(/\s+/, FIELDS.size)).
|
132
151
|
map{|(key, type), value| type.call(value)}
|
133
|
-
|
134
152
|
instance = self.new(*record)
|
135
153
|
|
136
154
|
processes[instance.process_id] = instance
|
@@ -151,12 +169,8 @@ module Process
|
|
151
169
|
end
|
152
170
|
end
|
153
171
|
|
154
|
-
if
|
172
|
+
if memory
|
155
173
|
self.capture_memory(processes)
|
156
|
-
|
157
|
-
# if pid
|
158
|
-
# self.compute_summary(pid, processes)
|
159
|
-
# end
|
160
174
|
end
|
161
175
|
|
162
176
|
return processes
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2025, by Samuel Williams.
|
5
|
+
|
6
|
+
module Process
|
7
|
+
module Metrics
|
8
|
+
class Memory::Darwin
|
9
|
+
VMMAP = "/usr/bin/vmmap"
|
10
|
+
|
11
|
+
# Whether the memory usage can be captured on this system.
|
12
|
+
def self.supported?
|
13
|
+
File.executable?(VMMAP)
|
14
|
+
end
|
15
|
+
|
16
|
+
# @returns [Numeric] Total memory size in kilobytes.
|
17
|
+
def self.total_size
|
18
|
+
# sysctl hw.memsize
|
19
|
+
IO.popen(["sysctl", "hw.memsize"], "r") do |io|
|
20
|
+
io.each_line do |line|
|
21
|
+
if line =~ /hw.memsize: (\d+)/
|
22
|
+
return $1.to_i / 1024
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Parse a size string into kilobytes.
|
29
|
+
def self.parse_size(string)
|
30
|
+
return 0 unless string
|
31
|
+
|
32
|
+
case string.strip
|
33
|
+
when /([\d\.]+)K/i then ($1.to_f).round
|
34
|
+
when /([\d\.]+)M/i then ($1.to_f * 1024).round
|
35
|
+
when /([\d\.]+)G/i then ($1.to_f * 1024 * 1024).round
|
36
|
+
else (string.to_f / 1024).ceil
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
LINE = /\A
|
41
|
+
\s*
|
42
|
+
(?<region_name>.+?)\s+
|
43
|
+
(?<start_address>[0-9a-fA-F]+)-(?<end_address>[0-9a-fA-F]+)\s+
|
44
|
+
\[\s*(?<virtual_size>[\d\.]+[KMG]?)\s+(?<resident_size>[\d\.]+[KMG]?)\s+(?<dirty_size>[\d\.]+[KMG]?)\s+(?<swap_size>[\d\.]+[KMG]?)\s*\]\s+
|
45
|
+
(?<permissions>[rwx\-\/]+)\s+
|
46
|
+
SM=(?<sharing_mode>\w+)
|
47
|
+
/x
|
48
|
+
|
49
|
+
# Capture memory usage for the given process IDs.
|
50
|
+
def self.capture(pid, count: 1, **options)
|
51
|
+
usage = Memory.zero
|
52
|
+
|
53
|
+
IO.popen(["vmmap", pid.to_s], "r") do |io|
|
54
|
+
io.each_line do |line|
|
55
|
+
if match = LINE.match(line)
|
56
|
+
virtual_size = parse_size(match[:virtual_size])
|
57
|
+
resident_size = parse_size(match[:resident_size])
|
58
|
+
dirty_size = parse_size(match[:dirty_size])
|
59
|
+
swap_size = parse_size(match[:swap_size])
|
60
|
+
|
61
|
+
# Update counts
|
62
|
+
usage.map_count += 1
|
63
|
+
usage.resident_size += resident_size
|
64
|
+
usage.swap_size += swap_size
|
65
|
+
|
66
|
+
# Private vs. Shared memory
|
67
|
+
# COW=copy_on_write PRV=private NUL=empty ALI=aliased
|
68
|
+
# SHM=shared ZER=zero_filled S/A=shared_alias
|
69
|
+
case match[:sharing_mode]
|
70
|
+
when "PRV"
|
71
|
+
usage.private_clean_size += resident_size - dirty_size
|
72
|
+
usage.private_dirty_size += dirty_size
|
73
|
+
when "COW", "SHM"
|
74
|
+
usage.shared_clean_size += resident_size - dirty_size
|
75
|
+
usage.shared_dirty_size += dirty_size
|
76
|
+
end
|
77
|
+
|
78
|
+
# Anonymous memory: no region detail path or special names
|
79
|
+
if match[:region_name] =~ /MALLOC|VM_ALLOCATE|Stack|STACK|anonymous/
|
80
|
+
usage.anonymous_size += resident_size
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Darwin does not expose proportional memory usage, so we guess based on the number of processes. Yes, this is a terrible hack, but it's the most reasonable thing to do given the constraints:
|
87
|
+
usage.proportional_size = usage.resident_size / count
|
88
|
+
usage.proportional_swap_size = usage.swap_size / count
|
89
|
+
|
90
|
+
return usage
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
if Memory::Darwin.supported?
|
95
|
+
class << Memory
|
96
|
+
def supported?
|
97
|
+
return true
|
98
|
+
end
|
99
|
+
|
100
|
+
def total_size
|
101
|
+
return Memory::Darwin.total_size
|
102
|
+
end
|
103
|
+
|
104
|
+
def capture(...)
|
105
|
+
return Memory::Darwin.capture(...)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Released under the MIT License.
|
4
|
+
# Copyright, 2025, by Samuel Williams.
|
5
|
+
|
6
|
+
module Process
|
7
|
+
module Metrics
|
8
|
+
class Memory::Linux
|
9
|
+
# @returns [Numeric] Total memory size in kilobytes.
|
10
|
+
def self.total_size
|
11
|
+
File.read("/proc/meminfo").each_line do |line|
|
12
|
+
if /MemTotal:\s+(?<total>\d+) kB/ =~ line
|
13
|
+
return total.to_i
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# The fields that will be extracted from the `smaps` data.
|
19
|
+
SMAP = {
|
20
|
+
"Rss" => :resident_size,
|
21
|
+
"Pss" => :proportional_size,
|
22
|
+
"Shared_Clean" => :shared_clean_size,
|
23
|
+
"Shared_Dirty" => :shared_dirty_size,
|
24
|
+
"Private_Clean" => :private_clean_size,
|
25
|
+
"Private_Dirty" => :private_dirty_size,
|
26
|
+
"Referenced" => :referenced_size,
|
27
|
+
"Anonymous" => :anonymous_size,
|
28
|
+
"Swap" => :swap_size,
|
29
|
+
"SwapPss" => :proportional_swap_size,
|
30
|
+
}
|
31
|
+
|
32
|
+
if File.readable?("/proc/self/smaps_rollup")
|
33
|
+
# Whether the memory usage can be captured on this system.
|
34
|
+
def self.supported?
|
35
|
+
true
|
36
|
+
end
|
37
|
+
|
38
|
+
# Capture memory usage for the given process IDs.
|
39
|
+
def self.capture(pids)
|
40
|
+
usage = Memory.zero
|
41
|
+
|
42
|
+
pids.each do |pid|
|
43
|
+
File.foreach("/proc/#{pid}/smaps_rollup") do |line|
|
44
|
+
if /(?<name>.*?):\s+(?<value>\d+) kB/ =~ line
|
45
|
+
if key = SMAP[name]
|
46
|
+
usage[key] += value.to_i
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
usage.map_count += File.readlines("/proc/#{pid}/maps").size
|
52
|
+
rescue Errno::ENOENT => error
|
53
|
+
# Ignore.
|
54
|
+
end
|
55
|
+
|
56
|
+
return usage
|
57
|
+
end
|
58
|
+
elsif File.readable?("/proc/self/smaps")
|
59
|
+
# Whether the memory usage can be captured on this system.
|
60
|
+
def self.supported?
|
61
|
+
true
|
62
|
+
end
|
63
|
+
|
64
|
+
# Capture memory usage for the given process IDs.
|
65
|
+
def self.capture(pid, **options)
|
66
|
+
usage = Memory.zero
|
67
|
+
|
68
|
+
begin
|
69
|
+
File.foreach("/proc/#{pid}/smaps") do |line|
|
70
|
+
# The format of this is fixed according to:
|
71
|
+
# https://github.com/torvalds/linux/blob/351c8a09b00b5c51c8f58b016fffe51f87e2d820/fs/proc/task_mmu.c#L804-L814
|
72
|
+
if /(?<name>.*?):\s+(?<value>\d+) kB/ =~ line
|
73
|
+
if key = SMAP[name]
|
74
|
+
usage[key] += value.to_i
|
75
|
+
end
|
76
|
+
elsif /VmFlags:\s+(?<flags>.*)/ =~ line
|
77
|
+
# It should be possible to extract the number of fibers and each fiber's memory usage.
|
78
|
+
# flags = flags.split(/\s+/)
|
79
|
+
usage.map_count += 1
|
80
|
+
end
|
81
|
+
end
|
82
|
+
rescue Errno::ENOENT => error
|
83
|
+
# Ignore.
|
84
|
+
end
|
85
|
+
|
86
|
+
return usage
|
87
|
+
end
|
88
|
+
else
|
89
|
+
def self.supported?
|
90
|
+
false
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
if Memory::Linux.supported?
|
96
|
+
class << Memory
|
97
|
+
def supported?
|
98
|
+
return true
|
99
|
+
end
|
100
|
+
|
101
|
+
def total_size
|
102
|
+
return Memory::Linux.total_size
|
103
|
+
end
|
104
|
+
|
105
|
+
def capture(...)
|
106
|
+
return Memory::Linux.capture(...)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -1,12 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2019-
|
4
|
+
# Copyright, 2019-2025, by Samuel Williams.
|
5
5
|
|
6
|
-
require
|
6
|
+
require "json"
|
7
7
|
|
8
8
|
module Process
|
9
9
|
module Metrics
|
10
|
+
# Represents memory usage for a process, sizes are in kilobytes.
|
10
11
|
class Memory < Struct.new(:map_count, :resident_size, :proportional_size, :shared_clean_size, :shared_dirty_size, :private_clean_size, :private_dirty_size, :referenced_size, :anonymous_size, :swap_size, :proportional_swap_size)
|
11
12
|
|
12
13
|
alias as_json to_h
|
@@ -26,87 +27,22 @@ module Process
|
|
26
27
|
self.private_clean_size + self.private_dirty_size
|
27
28
|
end
|
28
29
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
"Referenced" => :referenced_size,
|
38
|
-
"Anonymous" => :anonymous_size,
|
39
|
-
"Swap" => :swap_size,
|
40
|
-
"SwapPss" => :proportional_swap_size,
|
41
|
-
}
|
30
|
+
def self.zero
|
31
|
+
self.new(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Whether the memory usage can be captured on this system.
|
35
|
+
def self.supported?
|
36
|
+
false
|
37
|
+
end
|
42
38
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
true
|
47
|
-
end
|
48
|
-
|
49
|
-
# Capture memory usage for the given process IDs.
|
50
|
-
def self.capture(pids)
|
51
|
-
usage = self.new(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
|
52
|
-
|
53
|
-
pids.each do |pid|
|
54
|
-
File.foreach("/proc/#{pid}/smaps_rollup") do |line|
|
55
|
-
if /(?<name>.*?):\s+(?<value>\d+) kB/ =~ line
|
56
|
-
if key = MAP[name]
|
57
|
-
usage[key] += value.to_i
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
usage.map_count += File.readlines("/proc/#{pid}/maps").size
|
63
|
-
rescue Errno::ENOENT => error
|
64
|
-
# Ignore.
|
65
|
-
end
|
66
|
-
|
67
|
-
return usage
|
68
|
-
end
|
69
|
-
elsif File.readable?('/proc/self/smaps')
|
70
|
-
# Whether the memory usage can be captured on this system.
|
71
|
-
def self.supported?
|
72
|
-
true
|
73
|
-
end
|
74
|
-
|
75
|
-
# Capture memory usage for the given process IDs.
|
76
|
-
def self.capture(pids)
|
77
|
-
usage = self.new(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
|
78
|
-
|
79
|
-
pids.each do |pid|
|
80
|
-
File.foreach("/proc/#{pid}/smaps") do |line|
|
81
|
-
# The format of this is fixed according to:
|
82
|
-
# https://github.com/torvalds/linux/blob/351c8a09b00b5c51c8f58b016fffe51f87e2d820/fs/proc/task_mmu.c#L804-L814
|
83
|
-
if /(?<name>.*?):\s+(?<value>\d+) kB/ =~ line
|
84
|
-
if key = MAP[name]
|
85
|
-
usage[key] += value.to_i
|
86
|
-
end
|
87
|
-
elsif /VmFlags:\s+(?<flags>.*)/ =~ line
|
88
|
-
# It should be possible to extract the number of fibers and each fiber's memory usage.
|
89
|
-
# flags = flags.split(/\s+/)
|
90
|
-
usage.map_count += 1
|
91
|
-
end
|
92
|
-
end
|
93
|
-
rescue Errno::ENOENT => error
|
94
|
-
# Ignore.
|
95
|
-
end
|
96
|
-
|
97
|
-
return usage
|
98
|
-
end
|
99
|
-
else
|
100
|
-
# Whether the memory usage can be captured on this system.
|
101
|
-
def self.supported?
|
102
|
-
false
|
103
|
-
end
|
104
|
-
|
105
|
-
# Capture memory usage for the given process IDs.
|
106
|
-
def self.capture(pids)
|
107
|
-
return self.new
|
108
|
-
end
|
39
|
+
# Capture memory usage for the given process IDs.
|
40
|
+
def self.capture(pid, **options)
|
41
|
+
return nil
|
109
42
|
end
|
110
43
|
end
|
111
44
|
end
|
112
45
|
end
|
46
|
+
|
47
|
+
require_relative "memory/linux"
|
48
|
+
require_relative "memory/darwin"
|
data/license.md
CHANGED
data/readme.md
CHANGED
@@ -24,8 +24,8 @@ We welcome contributions to this project.
|
|
24
24
|
|
25
25
|
### Developer Certificate of Origin
|
26
26
|
|
27
|
-
|
27
|
+
In order to protect users of this project, we require all contributors to comply with the [Developer Certificate of Origin](https://developercertificate.org/). This ensures that all contributions are properly licensed and attributed.
|
28
28
|
|
29
|
-
###
|
29
|
+
### Community Guidelines
|
30
30
|
|
31
|
-
This project is
|
31
|
+
This project is best served by a collaborative and respectful environment. Treat each other professionally, respect differing viewpoints, and engage constructively. Harassment, discrimination, or harmful behavior is not tolerated. Communicate clearly, listen actively, and support one another. If any issues arise, please inform the project maintainers.
|
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,12 +1,11 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: process-metrics
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
8
8
|
- Adam Daniels
|
9
|
-
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain:
|
12
11
|
- |
|
@@ -38,7 +37,7 @@ cert_chain:
|
|
38
37
|
Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
|
39
38
|
voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
|
40
39
|
-----END CERTIFICATE-----
|
41
|
-
date:
|
40
|
+
date: 2025-02-21 00:00:00.000000000 Z
|
42
41
|
dependencies:
|
43
42
|
- !ruby/object:Gem::Dependency
|
44
43
|
name: console
|
@@ -55,35 +54,33 @@ dependencies:
|
|
55
54
|
- !ruby/object:Gem::Version
|
56
55
|
version: '1.8'
|
57
56
|
- !ruby/object:Gem::Dependency
|
58
|
-
name:
|
57
|
+
name: json
|
59
58
|
requirement: !ruby/object:Gem::Requirement
|
60
59
|
requirements:
|
61
60
|
- - "~>"
|
62
61
|
- !ruby/object:Gem::Version
|
63
|
-
version: '2
|
62
|
+
version: '2'
|
64
63
|
type: :runtime
|
65
64
|
prerelease: false
|
66
65
|
version_requirements: !ruby/object:Gem::Requirement
|
67
66
|
requirements:
|
68
67
|
- - "~>"
|
69
68
|
- !ruby/object:Gem::Version
|
70
|
-
version: '2
|
69
|
+
version: '2'
|
71
70
|
- !ruby/object:Gem::Dependency
|
72
|
-
name:
|
71
|
+
name: samovar
|
73
72
|
requirement: !ruby/object:Gem::Requirement
|
74
73
|
requirements:
|
75
74
|
- - "~>"
|
76
75
|
- !ruby/object:Gem::Version
|
77
|
-
version: '2'
|
76
|
+
version: '2.1'
|
78
77
|
type: :runtime
|
79
78
|
prerelease: false
|
80
79
|
version_requirements: !ruby/object:Gem::Requirement
|
81
80
|
requirements:
|
82
81
|
- - "~>"
|
83
82
|
- !ruby/object:Gem::Version
|
84
|
-
version: '2'
|
85
|
-
description:
|
86
|
-
email:
|
83
|
+
version: '2.1'
|
87
84
|
executables:
|
88
85
|
- process-metrics
|
89
86
|
extensions: []
|
@@ -96,6 +93,8 @@ files:
|
|
96
93
|
- lib/process/metrics/command/top.rb
|
97
94
|
- lib/process/metrics/general.rb
|
98
95
|
- lib/process/metrics/memory.rb
|
96
|
+
- lib/process/metrics/memory/darwin.rb
|
97
|
+
- lib/process/metrics/memory/linux.rb
|
99
98
|
- lib/process/metrics/version.rb
|
100
99
|
- license.md
|
101
100
|
- readme.md
|
@@ -106,7 +105,6 @@ metadata:
|
|
106
105
|
documentation_uri: https://socketry.github.io/process-metrics/
|
107
106
|
funding_uri: https://github.com/sponsors/ioquatix
|
108
107
|
source_code_uri: https://github.com/socketry/process-metrics.git
|
109
|
-
post_install_message:
|
110
108
|
rdoc_options: []
|
111
109
|
require_paths:
|
112
110
|
- lib
|
@@ -114,15 +112,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
114
112
|
requirements:
|
115
113
|
- - ">="
|
116
114
|
- !ruby/object:Gem::Version
|
117
|
-
version: '
|
115
|
+
version: '3.1'
|
118
116
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
119
117
|
requirements:
|
120
118
|
- - ">="
|
121
119
|
- !ruby/object:Gem::Version
|
122
120
|
version: '0'
|
123
121
|
requirements: []
|
124
|
-
rubygems_version: 3.
|
125
|
-
signing_key:
|
122
|
+
rubygems_version: 3.6.2
|
126
123
|
specification_version: 4
|
127
124
|
summary: Provide detailed OS-specific process metrics.
|
128
125
|
test_files: []
|
metadata.gz.sig
CHANGED
Binary file
|