class PhusionPassenger::AdminTools::MemoryStats

Public Instance Methods

apache_processes() click to toggle source

Returns a list of Apache processes, which may be the empty list if Apache is not running. If the Apache executable name is unknown then nil will be returned.

# File lib/phusion_passenger/admin_tools/memory_stats.rb, line 64
def apache_processes
        @apache_processes ||= begin
                if PlatformInfo.httpd
                        processes = list_processes(:exe => PlatformInfo.httpd)
                        if processes.empty?
                                # On some Linux distros, the Apache worker processes
                                # are called "httpd.worker"
                                processes = list_processes(:exe => "#{PlatformInfo.httpd}.worker")
                        end
                        processes
                else
                        nil
                end
        end
end
nginx_processes() click to toggle source

Returns a list of Nginx processes, which may be the empty list if Nginx is not running.

# File lib/phusion_passenger/admin_tools/memory_stats.rb, line 82
def nginx_processes
        @nginx_processes ||= list_processes(:exe => "nginx")
end
passenger_processes() click to toggle source

Returns a list of Phusion Passenger processes, which may be the empty list if Phusion Passenger is not running.

# File lib/phusion_passenger/admin_tools/memory_stats.rb, line 88
def passenger_processes
        @passenger_processes ||= list_processes(:match =>
                /((^| )Passenger |(^| )Rails:|(^| )Rack:|PassengerHelperAgent|PassengerWatchdog|PassengerLoggingAgent|wsgi-loader.py)/)
end
platform_provides_private_dirty_rss_information?() click to toggle source
# File lib/phusion_passenger/admin_tools/memory_stats.rb, line 117
def platform_provides_private_dirty_rss_information?
        return ruby_platform =~ /linux/
end
root_privileges_required_for_private_dirty_rss?() click to toggle source

Returns whether root privileges are required in order to measure private dirty RSS. Only meaningful if platform_provides_private_dirty_rss_information? returns true.

# File lib/phusion_passenger/admin_tools/memory_stats.rb, line 123
def root_privileges_required_for_private_dirty_rss?
        all_processes = (apache_processes || []) + nginx_processes + passenger_processes
        return all_processes.any?{ |p| p.private_dirty_rss.nil? }
end
should_show_private_dirty_rss?() click to toggle source
# File lib/phusion_passenger/admin_tools/memory_stats.rb, line 128
def should_show_private_dirty_rss?
        return platform_provides_private_dirty_rss_information? &&
                (::Process.euid == 0 || root_privileges_required_for_private_dirty_rss?)
end
sum_memory_usage(processes) click to toggle source

Returns the sum of the memory usages of all given processes. Returns a pair [usage, accurate]. usage is the summed memory usage in KB, and accurate indicates whether this sum is accurate. This may be false if some process's memory usage cannot be determined.

# File lib/phusion_passenger/admin_tools/memory_stats.rb, line 97
def sum_memory_usage(processes)
        total = 0
        if should_show_private_dirty_rss?
                accurate = true
                processes.each do |p|
                        if p.private_dirty_rss.is_a?(Numeric)
                                total += p.private_dirty_rss
                        else
                                accurate = true
                        end
                end
                return [total, accurate]
        else
                processes.each do |p|
                        total += p.rss
                end
                return [total, true]
        end
end
system_ram_usage() click to toggle source

Determine the system's RAM usage, not including swap. Returns a tuple [total, used] where both numbers are in KB, or nil if the system's RAM usage cannot be determined.

# File lib/phusion_passenger/admin_tools/memory_stats.rb, line 136
def system_ram_usage
        @total_system_ram ||= begin
                case ruby_platform
                when /linux/
                        free_text = %x`free -k`
                        
                        free_text =~ %r{Mem:(.+)$}
                        line = $1.strip
                        total = line.split(/ +/).first.to_i
                        
                        free_text =~ %r{buffers/cache:(.+)$}
                        line = $1.strip
                        used = line.split(/ +/).first.to_i
                        
                        [total, used]
                when /darwin/
                        vm_stat = %x`vm_stat`
                        vm_stat =~ /page size of (\d+) bytes/
                        page_size = $1
                        vm_stat =~ /Pages free: +(\d+)/
                        free = $1
                        vm_stat =~ /Pages active: +(\d+)/
                        active = $1
                        vm_stat =~ /Pages inactive: +(\d+)/
                        inactive = $1
                        vm_stat =~ /Pages wired down: +(\d+)/
                        wired = $1
                        
                        if page_size && free && active && inactive && wired
                                page_size = page_size.to_i
                                free = free.to_i * page_size / 1024
                                active = active.to_i * page_size / 1024
                                inactive = inactive.to_i * page_size / 1024
                                wired = wired.to_i * page_size / 1024
                                
                                used = active + wired
                                [free + inactive + used, used]
                        else
                                nil
                        end
                else
                        %x`top` =~ /(\d+)(K|M) Active, (\d+)(K|M) Inact, (\d+)(K|M) Wired,.*?(\d+)(K|M) Free/
                        if $1 && $2 && $3 && $4 && $5 && $6 && $7 && $8
                                to_kb = lambda do |number, unit|
                                        if unit == 'K'
                                                number.to_i
                                        else
                                                number.to_i * 1024
                                        end
                                end
                                
                                active = to_kb.call($1, $2)
                                inactive = to_kb.call($3, $4)
                                wired = to_kb.call($5, $6)
                                free = to_kb.call($7, $8)
                                
                                used = active + wired
                                [free + inactive + used, used]
                        else
                                nil
                        end
                end
        end
end

Private Instance Methods

determine_private_dirty_rss(pid) click to toggle source

Returns the private dirty RSS for the given process, in KB.

# File lib/phusion_passenger/admin_tools/memory_stats.rb, line 280
def determine_private_dirty_rss(pid)
        total = 0
        File.read("/proc/#{pid}/smaps").split("\n").each do |line|
                line =~ /^(Private)_Dirty: +(\d+)/
                if $2
                        total += $2.to_i
                end
        end
        if total == 0
                return nil
        else
                return total
        end
rescue Errno::EACCES, Errno::ENOENT
        return nil
end
list_processes(options) click to toggle source

Returns a list of Process objects that match the given search criteria.

# Search by executable path.
list_processes(:exe => '/usr/sbin/apache2')

# Search by executable name.
list_processes(:name => 'ruby1.8')

# Search by process name.
list_processes(:match => 'Passenger FrameworkSpawner')
# File lib/phusion_passenger/admin_tools/memory_stats.rb, line 216
def list_processes(options)
        if options[:exe]
                name = options[:exe].sub(/.*\/(.*)/, '\1')
                if ruby_platform =~ /linux/
                        ps = "ps -C '#{name}'"
                else
                        ps = "ps -A"
                        options[:match] = Regexp.new(Regexp.escape(name))
                end
        elsif options[:name]
                if ruby_platform =~ /linux/
                        ps = "ps -C '#{options[:name]}'"
                else
                        ps = "ps -A"
                        options[:match] = Regexp.new(" #{Regexp.escape(options[:name])}")
                end
        elsif options[:match]
                ps = "ps -A"
        else
                raise ArgumentError, "Invalid options."
        end
        
        processes = []
        case RUBY_PLATFORM
        when /solaris/
                list = %x`#{ps} -o pid,ppid,nlwp,vsz,rss,pcpu,comm`.split("\n")
                threads_known = true
        when /darwin/
                list = %x`#{ps} -w -o pid,ppid,vsz,rss,%cpu,command`.split("\n")
                threads_known = false
        else
                list = %x`#{ps} -w -o pid,ppid,nlwp,vsz,rss,%cpu,command`.split("\n")
                threads_known = true
        end
        list.shift
        list.each do |line|
                line.gsub!(/^ */, '')
                line.gsub!(/ *$/, '')
                
                p = Process.new
                if threads_known
                        p.pid, p.ppid, p.threads, p.vm_size, p.rss, p.cpu, p.name = line.split(/ +/, 7)
                else
                        p.pid, p.ppid, p.vm_size, p.rss, p.cpu, p.name = line.split(/ +/, 6)
                end
                p.name.sub!(/\Aruby: /, '')
                p.name.sub!(/ \(ruby\)\Z/, '')
                if p.name !~ /^ps/ && (!options[:match] || p.name.match(options[:match]))
                        # Convert some values to integer.
                        [:pid, :ppid, :vm_size, :rss].each do |attr|
                                p.send("#{attr}=", p.send(attr).to_i)
                        end
                        p.threads = p.threads.to_i if threads_known

                        if platform_provides_private_dirty_rss_information?
                                p.private_dirty_rss = determine_private_dirty_rss(p.pid)
                        end
                        processes << p
                end
        end
        return processes
end
ruby_platform() click to toggle source
# File lib/phusion_passenger/admin_tools/memory_stats.rb, line 202
def ruby_platform
        return RUBY_PLATFORM
end