class Sys::ProcTable
The ProcTable
class encapsulates process table information.
The ProcTable
class encapsulates process table information.
The ProcTable
class encapsulates process table information.
The ProcTable
class encapsulates process table information
Constants
- COMMLEN
- CTL_KERN
- KERN_PROCARGS
- KERN_PROCARGS2
- KERN_PROC_PID
- KERN_PROC_PROC
- KI_EMULNAMELEN
- KI_NGROUPS
- KI_NSPARE_INT
- KI_NSPARE_LONG
- KI_NSPARE_PTR
- LOCKNAMELEN
- LOGNAMELEN
- MAXCOMLEN
- MAXPATHLEN
- MAXTHREADNAMESIZE
- OCOMMLEN
- POSIX_ARG_MAX
- PRNODEV
- PROC_PIDLISTTHREADS
- PROC_PIDPATHINFO_MAXSIZE
- PROC_PIDTASKALLINFO
There is no constructor
- PROC_PIDTHREADINFO
- PROC_STRUCT_FIELD_MAP
- PR_MFLAGS
prmap_t pr_mflags
- ProcTableMapStruct
- ProcTableStruct
Add a couple aliases to make it similar to Linux
- SIDL
- SLOCK
- SRUN
- SSLEEP
- SSTOP
- SWAIT
- SZOMB
- S_IFCHR
- ThreadInfoStruct
- VERSION
The version of the sys-proctable library
- WMESGLEN
Public Class Methods
Returns an array of fields that each ProcTableStruct
will contain. This may be useful if you want to know in advance what fields are available without having to perform at least one read of the /proc table.
Example:
Sys::ProcTable.fields.each do |field| puts "Field: #{field}" end
# File lib/aix/sys/proctable.rb, line 454 def self.fields @fields.map{ |f| f.to_s } end
In block form, yields a ProcTableStruct
for each process entry that you have rights to. This method returns an array of ProcTableStruct's in non-block form.
If a pid
is provided, then only a single ProcTableStruct
is yielded or returned, or nil if no process information is found for that pid
.
Example:
# Iterate over all processes ProcTable.ps do |proc_info| p proc_info end # Print process table information for only pid 1001 p ProcTable.ps(pid: 1001)
# File lib/aix/sys/proctable.rb, line 207 def self.ps(**kwargs) pid = kwargs[:pid] raise TypeError unless pid.is_a?(Numeric) if pid array = block_given? ? nil : [] struct = nil Dir.foreach("/proc") do |file| next if file =~ /\D/ # Skip non-numeric entries under /proc # Only return information for a given pid, if provided if pid next unless file.to_i == pid end # Skip over any entries we don't have permissions to read next unless File.readable?("/proc/#{file}/psinfo") psinfo = IO.read("/proc/#{file}/psinfo") rescue next psinfo_array = psinfo.unpack(@psinfo_pack_directive) struct = ProcTableStruct.new struct.flag = psinfo_array[0] # pr_flag struct.flag2 = psinfo_array[1] # pr_flag2 struct.nlwp = psinfo_array[2] # pr_nlwp # pr__pad1 struct.uid = psinfo_array[4] # pr_uid struct.euid = psinfo_array[5] # pr_euid struct.gid = psinfo_array[6] # pr_gid struct.egid = psinfo_array[7] # pr_egid struct.pid = psinfo_array[8] # pr_pid struct.ppid = psinfo_array[9] # pr_ppid struct.pgid = psinfo_array[10] # pr_pgid struct.sid = psinfo_array[11] # pr_sid struct.ttydev = psinfo_array[12] # pr_ttydev # convert from 64-bit dev_t to 32-bit dev_t and then map the device # number to a name ttydev = struct.ttydev ttydev = (((ttydev & 0x0000FFFF00000000) >> 16) | (ttydev & 0xFFFF)) struct.s_ttydev = @devs.has_key?(ttydev) ? @devs[ttydev] : '-' struct.addr = psinfo_array[13] # pr_addr struct.size = psinfo_array[14] * 1024 # pr_size struct.rssize = psinfo_array[15] * 1024 # pr_rssize struct.start = Time.at(psinfo_array[16], psinfo_array[17]) # pr_start # skip pr_start.__pad struct.time = psinfo_array[19] # pr_time # skip pr_time.tv_nsec and pr_time.__pad struct.cid = psinfo_array[22] # pr_cid # skip pr__pad2 struct.argc = psinfo_array[24] # pr_argc struct.argv = psinfo_array[25] # pr_argv struct.envp = psinfo_array[26] # pr_envp struct.fname = psinfo_array[27] # pr_fname struct.psargs = psinfo_array[28] # pr_psargs # skip pr__pad ### lwpsinfo_t info struct.lwpid = psinfo_array[37] # pr_lwpid # skip pr_addr struct.wchan = psinfo_array[39] # pr_wchan # skip pr_flag struct.wtype = psinfo_array[41] # pr_wtype struct.state = psinfo_array[42] # pr_state struct.sname = psinfo_array[43] # pr_sname struct.nice = psinfo_array[44] # pr_nice struct.pri = psinfo_array[45] # pr_pri struct.policy = psinfo_array[46] # pr_policy struct.clname = psinfo_array[47] # pr_clname struct.onpro = psinfo_array[48] # pr_onpro struct.bindpro = psinfo_array[49] # pr_bindpro struct.ptid = psinfo_array[50] # pr_ptid # skip pr__pad1 # skip pr__pad # Get the full command line out of /proc/<pid>/as. begin File.open("/proc/#{file}/as", 'rb') do |fd| np = fd.sysseek(struct.argv, IO::SEEK_SET) if np != struct.argv raise Error, "argv seek to #{struct.argv}, result #{np}", caller end argv = fd.sysread(4).unpack('L')[0] np = fd.sysseek(argv, IO::SEEK_SET) if np != argv raise Error, "*argv seek to #{argv}, result #{np}", caller end argv = fd.sysread(4 * struct.argc).unpack("L#{struct.argc}") struct.cmd_args = [] argv.each_with_index do |address, i| np = fd.sysseek(address, IO::SEEK_SET) if np != address raise Error, "argv[#{i}] seek to #{address}, result #{np}", caller end data = fd.sysread(512)[/^[^\0]*/] # Null strip struct.cmd_args << data end # Get the environment hash associated with the process. struct.environ = {} # First have to go to the address given by struct.envp. That will # give us the address of the environment pointer array. np = fd.sysseek(struct.envp, IO::SEEK_SET) if np != struct.envp raise Error, "envp seek to #{struct.envp}, result #{np}", caller end envloc = fd.sysread(4).unpack('L')[0] n = 0 loop do np = fd.sysseek(envloc, IO::SEEK_SET) if np != envloc raise Error, "envp[#{n}] seek to #{envloc}, result #{np}", caller end envp = fd.sysread(4).unpack("L")[0] break if envp.zero? np = fd.sysseek(envp, IO::SEEK_SET) data = fd.sysread(1024)[/^[^\0]*/] # Null strip key, value = data.split('=') struct.environ[key] = value envloc += 4 n += 1 end end rescue Errno::EACCES, Errno::EOVERFLOW, EOFError # Skip this if we don't have proper permissions, if there's # no associated environment, or if there's a largefile issue. rescue Errno::ENOENT next # The process has terminated. Bail out! end # Information from /proc/<pid>/fd. This returns an array of # numeric file descriptors used by the process. struct.fd = Dir["/proc/#{file}/fd/*"].map { |f| File.basename(f).to_i } # Use the cmd_args as the cmdline if available. Otherwise use # the psargs. This struct member is provided to provide a measure # of consistency with the other platform implementations. if struct.cmd_args.nil? || struct.cmd_args.empty? struct.cmdline = struct.psargs else struct.cmdline = struct.cmd_args.join(' ') end # get current working directory from /proc/<pid>/cwd struct.cwd = File.readlink("/proc/#{file}/cwd") rescue nil # get virtual address map from /proc/<pid>/map begin struct.map = [] File.open("/proc/#{file}/map", 'rb') do |fd| loop do prmap_array = fd.sysread(176).unpack(@prmap_pack_directive) break if prmap_array[0].zero? map_struct = ProcTableMapStruct.new map_struct.size = prmap_array[0] # pr_size map_struct.vaddr = prmap_array[1] # pr_vaddr map_struct.mapname = prmap_array[2] # pr_mapname map_struct.off = prmap_array[3] # pr_off map_struct.mflags = prmap_array[4] # pr_mflags # convert pr_mflags value to string sort of like procmap outputs mflags = map_struct.mflags map_struct.s_mflags = '' sep = '' PR_MFLAGS.each do |flag| if (mflags & flag[0]).nonzero? map_struct.s_mflags << sep << flag[1] sep = '/' mflags &= ~flag[0] end end if mflags.nonzero? map_struct.s_mflags << sep << sprintf('%08x', mflags) end map_struct.pathoff = prmap_array[5] # pr_pathoff map_struct.alias = prmap_array[6] # pr_alias map_struct.gp = prmap_array[7] # pr_gp struct.map << map_struct end struct.map.each do |m| next if m.pathoff.zero? fd.sysseek(m.pathoff, IO::SEEK_SET) buf = fd.sysread(4096) buf =~ /^([^\0]*)\0([^\0]*)\0/ m.path = $2.empty? ? $1 : "#{$1}(#{$2})" end end struct.map = nil if struct.map.empty? rescue struct.map = nil end # This is read-only data struct.freeze if block_given? yield struct else array << struct end end pid ? struct : array end
Private Class Methods
Pass by reference method that updates the Ruby struct based on the FFI
struct.
# File lib/darwin/sys/proctable.rb, line 265 def self.apply_info_to_struct(info, struct) # Chop the leading xx_ from the FFI struct members for our ruby struct. info.members.each do |nested| info[nested].members.each do |member| if info[nested][member].is_a?(FFI::StructLayout::CharArray) struct[PROC_STRUCT_FIELD_MAP[member]] = info[nested][member].to_s else struct[PROC_STRUCT_FIELD_MAP[member]] = info[nested][member] end end end end
There is a bug in win32ole where uint64 types are returned as a String instead of a Fixnum. This method deals with that for now.
# File lib/windows/sys/proctable.rb, line 203 def self.convert(str) return nil if str.nil? # Return nil, not 0 return str.to_i end
Get the command line arguments, as well as the environment settings, for the given PID.
# File lib/darwin/sys/proctable.rb, line 334 def self.get_cmd_args_and_env(pid, struct) len = FFI::MemoryPointer.new(:size_t) mib = FFI::MemoryPointer.new(:int, 3) # Since we may not have access to the process information due # to improper privileges, just bail if we see a failure here. # First use KERN_PROCARGS2 to discover the argc value of the running process. mib.write_array_of_int([CTL_KERN, KERN_PROCARGS2, pid]) return if sysctl(mib, 3, nil, len, nil, 0) < 0 buf = FFI::MemoryPointer.new(:char, len.read_ulong) return if sysctl(mib, 3, buf, len, nil, 0) < 0 # The argc value is located in the first byte of buf argc = buf.read_bytes(1).ord buf.free # Now use KERN_PROCARGS to fetch the rest of the process information mib.write_array_of_int([CTL_KERN, KERN_PROCARGS, pid]) return if sysctl(mib, 3, nil, len, nil, 0) < 0 buf = FFI::MemoryPointer.new(:char, len.read_ulong) return if sysctl(mib, 3, buf, len, nil, 0) < 0 exe = buf.read_string # Read up to first null, does not include args struct[:exe] = exe # Parse the rest of the information out of a big, ugly string array = buf.read_bytes(len.read_ulong).split(0.chr) array.delete('') # Delete empty strings # The format that sysctl outputs is as follows: # # [full executable path] # [executable name] # [arguments] # [environment variables] # ... # \FF\BF # [full executable path] # # Strip the first executable path and the last two entries from the array. # What is left is the name, arguments, and environment variables array = array[1..-3] # It seems that argc sometimes returns a bogus value. In that case, delete # any environment variable strings, and reset the argc value. # if argc > array.size array.delete_if{ |e| e.include?('=') } argc = array.size end cmdline = '' # Extract the full command line and its arguments from the array argc.times do cmdline << ' ' + array.shift end struct[:cmdline] = cmdline.strip # Anything remaining at this point is a collection of key=value # pairs which we convert into a hash. environ = array.inject({}) do |hash, string| if string && string.include?('=') key, value = string.split('=') hash[key] = value end hash end struct[:environ] = environ end
Calculate the percentage of CPU usage for the given process.
# File lib/linux/sys/proctable.rb, line 315 def self.get_pctcpu(utime, start_time) return nil unless @boot_time hertz = 100.0 utime = (utime * 10000).to_f stime = (start_time.to_f / hertz) + @boot_time sprintf("%3.2f", (utime / 10000.0) / (Time.now.to_i - stime)).to_f end
Calculate the percentage of memory usage for the given process.
# File lib/linux/sys/proctable.rb, line 306 def self.get_pctmem(rss) return nil unless @mem_total page_size = 4096 rss_total = rss * page_size sprintf("%3.2f", (rss_total.to_f / @mem_total) * 100).to_f end
# File lib/freebsd/sys/proctable.rb, line 350 def self.get_state(int) case int when SIDL; "idle" when SRUN; "run" when SSLEEP; "sleep" when SSTOP; "stop" when SZOMB; "zombie" when SWAIT; "waiting" when SLOCK; "locked" else; "unknown" end end
Returns an array of ThreadInfo objects for the given pid.
# File lib/darwin/sys/proctable.rb, line 280 def self.get_thread_info(pid, struct, ptinfo) buf = FFI::MemoryPointer.new(:uint64_t, ptinfo[:pti_threadnum]) num = proc_pidinfo(pid, PROC_PIDLISTTHREADS, 0, buf, buf.size) if num <= 0 if [Errno::EPERM::Errno, Errno::ESRCH::Errno].include?(FFI.errno) return # Either we don't have permission, or the pid no longer exists else raise SystemCallError.new('proc_pidinfo', FFI.errno) end end max = ptinfo[:pti_threadnum] struct[:threadinfo] = [] 0.upto(max-1) do |index| tinfo = ProcThreadInfo.new # Use read_array_of_uint64 for compatibility with JRuby if necessary. if buf[index].respond_to?(:read_uint64) nb = proc_pidinfo(pid, PROC_PIDTHREADINFO, buf[index].read_uint64, tinfo, tinfo.size) else nb = proc_pidinfo(pid, PROC_PIDTHREADINFO, buf[index].read_array_of_uint64(1).first, tinfo, tinfo.size) end if nb <= 0 if [Errno::EPERM::Errno, Errno::ESRCH::Errno].include?(FFI.errno) return # Either we don't have permission, or the pid no longer exists else raise SystemCallError.new('proc_pidinfo', FFI.errno) end end tinfo_struct = ThreadInfoStruct.new( tinfo[:pth_user_time], tinfo[:pth_system_time], tinfo[:pth_cpu_usage], tinfo[:pth_policy], tinfo[:pth_run_state], tinfo[:pth_flags], tinfo[:pth_sleep_time], tinfo[:pth_curpri], tinfo[:pth_priority], tinfo[:pth_maxpriority], tinfo[:pth_name].to_s, ) struct[:threadinfo] << tinfo_struct end end
Converts a string in the format '20040703074625.015625-360' into a Ruby Time object.
# File lib/windows/sys/proctable.rb, line 194 def self.parse_ms_date(str) return if str.nil? return DateTime.parse(str) end