Including child processes in CPU/memory usage #30

Closed
wants to merge 2 commits into
from
Jump to file or symbol
Failed to load files and symbols.
+100 −7
Diff settings

Always

Just for now

@@ -13,6 +13,9 @@ module Conditions
# populated for Watches.
# +above+ is the percent CPU above which to trigger the condition. You
# may use #percent to clarify this amount (see examples).
+ # Optional
+ # +family+ should be true in order to include the CPU usage of all child
+ # processes.
#
# Examples
#
@@ -29,12 +32,13 @@ module Conditions
# c.pid_file = "/var/run/mongrel.3000.pid"
# end
class CpuUsage < PollCondition
- attr_accessor :above, :times, :pid_file
+ attr_accessor :above, :times, :pid_file, :family
def initialize
super
self.above = nil
self.times = [1, 1]
+ self.family = false
end
def prepare
@@ -62,7 +66,11 @@ def valid?
def test
process = System::Process.new(self.pid)
- @timeline.push(process.percent_cpu)
+ if self.family
+ @timeline.push(process.family_percent_cpu)
+ else
+ @timeline.push(process.percent_cpu)
+ end
history = "[" + @timeline.map { |x| "#{x > self.above ? '*' : ''}#{x}%%" }.join(", ") + "]"
@@ -77,4 +85,4 @@ def test
end
end
-end
+end
@@ -14,6 +14,9 @@ module Conditions
# the condition should trigger. You can also use the sugar
# methods #kilobytes, #megabytes, and #gigabytes to clarify
# this amount (see examples).
+ # Optional
+ # +family+ should be true in order to include the memory usage of all
+ # child processes.
#
# Examples
#
@@ -31,12 +34,13 @@ module Conditions
# c.pid_file = "/var/run/mongrel.3000.pid"
# end
class MemoryUsage < PollCondition
- attr_accessor :above, :times, :pid_file
+ attr_accessor :above, :times, :pid_file, :family
def initialize
super
self.above = nil
self.times = [1, 1]
+ self.family = false
end
def prepare
@@ -64,7 +68,11 @@ def valid?
def test
process = System::Process.new(self.pid)
- @timeline.push(process.memory)
+ if self.family
+ @timeline.push(process.family_memory)
+ else
+ @timeline.push(process.memory)
+ end
history = "[" + @timeline.map { |x| "#{x > self.above ? '*' : ''}#{x}kb" }.join(", ") + "]"
@@ -79,4 +87,4 @@ def test
end
end
-end
+end
View
@@ -24,6 +24,12 @@ def exists?
def memory
@poller.memory
end
+
+ # Memory usage in kilobytes (resident set size), including all child
+ # processes
+ def family_memory
+ family.map { |pid| fetch_system_poller.new(pid).memory } .reduce(:+)
+ end
# Percentage memory usage
def percent_memory
@@ -34,6 +40,11 @@ def percent_memory
def percent_cpu
@poller.percent_cpu
end
+
+ # Percentage CPU usage including all child processes
+ def family_percent_cpu
+ family.map { |pid| fetch_system_poller.new(pid).percent_cpu } .reduce(:+)
+ end
private
@@ -44,7 +55,20 @@ def fetch_system_poller
PortablePoller
end
end
- end
+ # Returns an array of PIDs of the polled process and all its children.
+ def family

This comment has been minimized.

@eric

eric Dec 17, 2011

Collaborator

This method should be moved into the PortablePoller and an implementation should be written for the SlashProcPoller.

@eric

eric Dec 17, 2011

Collaborator

This method should be moved into the PortablePoller and an implementation should be written for the SlashProcPoller.

+ stack = [@pid]
+ n = 0
+ while stack.count > n
+ pid = stack[n]
+ output = `ps -e -oppid=,pid= | grep '^#{pid}'`
+ children = output.split("\n").map { |x| x.sub(/^\d+\s/, '').to_i }
+ stack += children
+ n += 1
+ end
+ stack
+ end
+ end
end
end
@@ -19,12 +19,21 @@ def test_memory
assert @process.memory > 0
end
+ def test_family_memory
+ assert_kind_of Integer, @process.family_memory
+ assert @process.family_memory > 0
+ end
+
def test_percent_memory
assert_kind_of Float, @process.percent_memory
end
def test_percent_cpu
assert_kind_of Float, @process.percent_cpu
end
+
+ def test_family_percent_cpu
+ assert_kind_of Float, @process.family_percent_cpu
+ end
end
@@ -0,0 +1,44 @@
+require 'drb/drb'
+require 'ostruct'
+require File.dirname(__FILE__) + '/helper'
+
+class TestSystemProcessFamily < Test::Unit::TestCase
+ DRUBY_URI = 'druby://localhost:38753'
+
+ def setup
+ pid = Process.pid
+ @process = System::Process.new(pid)
+ end
+
+ def test_family_memory_usage
+ pid = fork
+ if pid.nil?
+ my_process = System::Process.new(Process.pid)
+ mem_before = my_process.memory
+ # Allocate some memory.
+ data = (0..10_000).reduce('') { |acc, int| acc + int.to_s }
+ # Create an object which will be used to communicate with the parent
+ # process.
+ obj = OpenStruct.new :done => false,
+ :used_memory => my_process.memory - mem_before
+ DRb.start_service DRUBY_URI, obj
+ at_exit { DRb.stop_service }
+ sleep 0.1 while not obj.done
+ exit 0
+ else
+ begin
+ obj = DRbObject.new_with_uri DRUBY_URI
+ allocated_by_child = obj.used_memory
+ # Do the actual memory assertion.
+ assert (@process.family_memory - @process.memory) > allocated_by_child
+ # Ask the child process to die.
+ obj.done = true
+ rescue DRb::DRbConnError => e
+ # Child's DRb service isn't running yet. Retry.
+ sleep 0.1
+ retry
+ end
+ end
+ end
+end
+