Skip to content

Commit 89543f8

Browse files
Improved implementation of Memory.capture.
1 parent 47db50a commit 89543f8

3 files changed

Lines changed: 42 additions & 27 deletions

File tree

lib/process/metrics/memory/darwin.rb

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -51,18 +51,18 @@ def self.parse_size(string)
5151

5252
# Capture memory usage for the given process IDs.
5353
def self.capture(pid, count: 1, **options)
54-
usage = Memory.zero
55-
5654
IO.popen(["vmmap", pid.to_s], "r") do |io|
55+
usage = Memory.zero
56+
5757
io.each_line do |line|
5858
if match = LINE.match(line)
59+
usage.map_count += 1
60+
5961
virtual_size = parse_size(match[:virtual_size])
6062
resident_size = parse_size(match[:resident_size])
6163
dirty_size = parse_size(match[:dirty_size])
6264
swap_size = parse_size(match[:swap_size])
6365

64-
# Update counts
65-
usage.map_count += 1
6666
usage.resident_size += resident_size
6767
usage.swap_size += swap_size
6868

@@ -84,13 +84,21 @@ def self.capture(pid, count: 1, **options)
8484
end
8585
end
8686
end
87+
88+
if usage.map_count.zero?
89+
# vmap might not fail, but also might not return any data.
90+
return nil
91+
end
92+
93+
# 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:
94+
usage.proportional_size = usage.resident_size / count
95+
usage.proportional_swap_size = usage.swap_size / count
96+
97+
return usage
8798
end
88-
89-
# 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:
90-
usage.proportional_size = usage.resident_size / count
91-
usage.proportional_swap_size = usage.swap_size / count
92-
93-
return usage
99+
rescue Errno::ESRCH
100+
# Process doesn't exist.
101+
return nil
94102
end
95103
end
96104

lib/process/metrics/memory/linux.rb

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ def self.capture_faults(pid, usage)
2020
usage.minor_faults = fields[10-3].to_i
2121
usage.major_faults = fields[12-3].to_i
2222
rescue
23-
# Be robust to unexpected formats; ignore errors silently.
23+
# Ignore.
2424
end
2525

2626
# @returns [Numeric] Total memory size in kilobytes.
@@ -54,10 +54,10 @@ def self.supported?
5454

5555
# Capture memory usage for the given process IDs.
5656
def self.capture(pid, **options)
57-
usage = Memory.zero
58-
59-
begin
60-
File.foreach("/proc/#{pid}/smaps_rollup") do |line|
57+
File.open("/proc/#{pid}/smaps_rollup") do |file|
58+
usage = Memory.zero
59+
60+
file.each_line do |line|
6161
if /(?<name>.*?):\s+(?<value>\d+) kB/ =~ line
6262
if key = SMAP[name]
6363
usage[key] += value.to_i
@@ -68,11 +68,12 @@ def self.capture(pid, **options)
6868
usage.map_count += File.readlines("/proc/#{pid}/maps").size
6969
# Also capture fault counters:
7070
self.capture_faults(pid, usage)
71-
rescue Errno::ENOENT, Errno::ESRCH
72-
# Ignore, process may have ended.
71+
72+
return usage
7373
end
74-
75-
return usage
74+
rescue Errno::ENOENT, Errno::ESRCH
75+
# Process doesn't exist.
76+
return nil
7677
end
7778
elsif File.readable?("/proc/self/smaps")
7879
# Whether the memory usage can be captured on this system.
@@ -82,10 +83,10 @@ def self.supported?
8283

8384
# Capture memory usage for the given process IDs.
8485
def self.capture(pid, **options)
85-
usage = Memory.zero
86-
87-
begin
88-
File.foreach("/proc/#{pid}/smaps") do |line|
86+
File.open("/proc/#{pid}/smaps") do |file|
87+
usage = Memory.zero
88+
89+
file.each_line do |line|
8990
# The format of this is fixed according to:
9091
# https://github.com/torvalds/linux/blob/351c8a09b00b5c51c8f58b016fffe51f87e2d820/fs/proc/task_mmu.c#L804-L814
9192
if /(?<name>.*?):\s+(?<value>\d+) kB/ =~ line
@@ -98,13 +99,15 @@ def self.capture(pid, **options)
9899
usage.map_count += 1
99100
end
100101
end
102+
101103
# Also capture fault counters:
102104
self.capture_faults(pid, usage)
103-
rescue Errno::ENOENT, Errno::ESRCH
104-
# Ignore, process may have ended.
105+
106+
return usage
105107
end
106-
107-
return usage
108+
rescue Errno::ENOENT, Errno::ESRCH
109+
# Process doesn't exist.
110+
return nil
108111
end
109112
else
110113
def self.supported?

releases.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Releases
22

3+
## Unreleased
4+
5+
- Be more proactive about returning nil if memory capture failed.
6+
37
## v0.6.1
48

59
- Handle `Errno::ESRCH: No such process @ io_fillbuf - fd:xxx /proc/xxx/smaps_rollup` by ignoring it.

0 commit comments

Comments
 (0)