Index: third_party/psutil/psutil/_pslinux.py |
diff --git a/third_party/psutil/psutil/_pslinux.py b/third_party/psutil/psutil/_pslinux.py |
new file mode 100755 |
index 0000000000000000000000000000000000000000..26a8abf55a6be0583b88f7a5dba320633c2168ea |
--- /dev/null |
+++ b/third_party/psutil/psutil/_pslinux.py |
@@ -0,0 +1,471 @@ |
+#!/usr/bin/env python |
+# |
+# $Id: _pslinux.py 800 2010-11-12 21:51:25Z g.rodola $ |
+# |
+ |
+__all__ = ["NUM_CPUS", "TOTAL_PHYMEM", |
+ "PlatformProcess", |
+ "avail_phymem", "used_phymem", "total_virtmem", "avail_virtmem", |
+ "used_virtmem", "get_system_cpu_times", "pid_exists", "get_pid_list", |
+ "phymem_buffers", "cached_phymem" |
+ ] |
+ |
+ |
+import os |
+import errno |
+import socket |
+import struct |
+import sys |
+import base64 |
+ |
+try: |
+ from collections import namedtuple |
+except ImportError: |
+ from psutil.compat import namedtuple # python < 2.6 |
+ |
+from psutil import _psposix |
+from psutil.error import AccessDenied, NoSuchProcess |
+ |
+ |
+def _get_uptime(): |
+ """Return system boot time (epoch in seconds)""" |
+ f = open('/proc/stat', 'r') |
+ for line in f: |
+ if line.startswith('btime'): |
+ f.close() |
+ return float(line.strip().split()[1]) |
+ |
+def _get_num_cpus(): |
+ """Return the number of CPUs on the system""" |
+ num = 0 |
+ f = open('/proc/cpuinfo', 'r') |
+ for line in f: |
+ if line.startswith('processor'): |
+ num += 1 |
+ f.close() |
+ return num |
+ |
+def _get_total_phymem(): |
+ """Return the total amount of physical memory, in bytes""" |
+ f = open('/proc/meminfo', 'r') |
+ for line in f: |
+ if line.startswith('MemTotal:'): |
+ f.close() |
+ return int(line.split()[1]) * 1024 |
+ |
+ |
+# Number of clock ticks per second |
+_CLOCK_TICKS = os.sysconf(os.sysconf_names["SC_CLK_TCK"]) |
+_UPTIME = _get_uptime() |
+NUM_CPUS = _get_num_cpus() |
+TOTAL_PHYMEM = _get_total_phymem() |
+ |
+del _get_uptime, _get_num_cpus, _get_total_phymem |
+ |
+# http://students.mimuw.edu.pl/lxr/source/include/net/tcp_states.h |
+_TCP_STATES_TABLE = {"01" : "ESTABLISHED", |
+ "02" : "SYN_SENT", |
+ "03" : "SYN_RECV", |
+ "04" : "FIN_WAIT1", |
+ "05" : "FIN_WAIT2", |
+ "06" : "TIME_WAIT", |
+ "07" : "CLOSE", |
+ "08" : "CLOSE_WAIT", |
+ "09" : "LAST_ACK", |
+ "0A" : "LISTEN", |
+ "0B" : "CLOSING" |
+ } |
+ |
+def avail_phymem(): |
+ """Return the amount of physical memory available, in bytes.""" |
+ f = open('/proc/meminfo', 'r') |
+ free = None |
+ _flag = False |
+ for line in f: |
+ if line.startswith('MemFree:'): |
+ free = int(line.split()[1]) * 1024 |
+ break |
+ f.close() |
+ return free |
+ |
+def used_phymem(): |
+ """"Return the amount of physical memory used, in bytes.""" |
+ return (TOTAL_PHYMEM - avail_phymem()) |
+ |
+def total_virtmem(): |
+ """"Return the total amount of virtual memory, in bytes.""" |
+ f = open('/proc/meminfo', 'r') |
+ for line in f: |
+ if line.startswith('SwapTotal:'): |
+ f.close() |
+ return int(line.split()[1]) * 1024 |
+ |
+def avail_virtmem(): |
+ """Return the amount of virtual memory currently in use on the |
+ system, in bytes. |
+ """ |
+ f = open('/proc/meminfo', 'r') |
+ for line in f: |
+ if line.startswith('SwapFree:'): |
+ f.close() |
+ return int(line.split()[1]) * 1024 |
+ |
+def used_virtmem(): |
+ """Return the amount of used memory currently in use on the system, |
+ in bytes. |
+ """ |
+ return total_virtmem() - avail_virtmem() |
+ |
+def cached_phymem(): |
+ """Return the amount of cached memory on the system, in bytes. |
+ This reflects the "cached" column of free command line utility. |
+ """ |
+ f = open('/proc/meminfo', 'r') |
+ for line in f: |
+ if line.startswith('Cached:'): |
+ f.close() |
+ return int(line.split()[1]) * 1024 |
+ |
+def phymem_buffers(): |
+ """Return the amount of physical memory buffers used by the |
+ kernel in bytes. |
+ This reflects the "buffers" column of free command line utility. |
+ """ |
+ f = open('/proc/meminfo', 'r') |
+ for line in f: |
+ if line.startswith('Buffers:'): |
+ f.close() |
+ return int(line.split()[1]) * 1024 |
+ |
+def get_system_cpu_times(): |
+ """Return a dict representing the following CPU times: |
+ user, nice, system, idle, iowait, irq, softirq. |
+ """ |
+ f = open('/proc/stat', 'r') |
+ values = f.readline().split() |
+ f.close() |
+ |
+ values = values[1:8] |
+ values = tuple([float(x) / _CLOCK_TICKS for x in values]) |
+ |
+ return dict(user=values[0], nice=values[1], system=values[2], idle=values[3], |
+ iowait=values[4], irq=values[5], softirq=values[6]) |
+ |
+def get_pid_list(): |
+ """Returns a list of PIDs currently running on the system.""" |
+ pids = [int(x) for x in os.listdir('/proc') if x.isdigit()] |
+ # special case for 0 (kernel process) PID |
+ pids.insert(0, 0) |
+ return pids |
+ |
+def pid_exists(pid): |
+ """Check For the existence of a unix pid.""" |
+ return _psposix.pid_exists(pid) |
+ |
+ |
+# --- decorators |
+ |
+def wrap_exceptions(callable): |
+ """Call callable into a try/except clause and translate ENOENT, |
+ EACCES and EPERM in NoSuchProcess or AccessDenied exceptions. |
+ """ |
+ def wrapper(self, *args, **kwargs): |
+ try: |
+ return callable(self, *args, **kwargs) |
+ except (OSError, IOError), err: |
+ if err.errno == errno.ENOENT: # no such file or directory |
+ raise NoSuchProcess(self.pid, self._process_name) |
+ if err.errno in (errno.EPERM, errno.EACCES): |
+ raise AccessDenied(self.pid, self._process_name) |
+ raise |
+ return wrapper |
+ |
+ |
+class LinuxProcess(object): |
+ """Linux process implementation.""" |
+ |
+ _meminfo_ntuple = namedtuple('meminfo', 'rss vms') |
+ _cputimes_ntuple = namedtuple('cputimes', 'user system') |
+ _openfile_ntuple = namedtuple('openfile', 'path fd') |
+ _connection_ntuple = namedtuple('connection', 'fd family type local_address ' |
+ 'remote_address status') |
+ __slots__ = ["pid", "_process_name"] |
+ |
+ def __init__(self, pid): |
+ self.pid = pid |
+ self._process_name = None |
+ |
+ @wrap_exceptions |
+ def get_process_name(self): |
+ if self.pid == 0: |
+ return 'sched' # special case for kernel process |
+ f = open("/proc/%s/stat" % self.pid) |
+ try: |
+ name = f.read().split(' ')[1].replace('(', '').replace(')', '') |
+ finally: |
+ f.close() |
+ # XXX - gets changed later and probably needs refactoring |
+ return name |
+ |
+ def get_process_exe(self): |
+ if self.pid in (0, 2): |
+ return "" # special case for kernel processes |
+ try: |
+ exe = os.readlink("/proc/%s/exe" % self.pid) |
+ except (OSError, IOError), err: |
+ if err.errno == errno.ENOENT: |
+ # no such file error; might be raised also if the |
+ # path actually exists for system processes with |
+ # low pids (about 0-20) |
+ if os.path.lexists("/proc/%s/exe" % self.pid): |
+ return "" |
+ else: |
+ # ok, it is a process which has gone away |
+ raise NoSuchProcess(self.pid, self._process_name) |
+ if err.errno in (errno.EPERM, errno.EACCES): |
+ raise AccessDenied(self.pid, self._process_name) |
+ raise |
+ |
+ # It seems symlinks can point to a deleted/invalid location |
+ # (this usually happens with "pulseaudio" process). |
+ # However, if we had permissions to execute readlink() it's |
+ # likely that we'll be able to figure out exe from argv[0] |
+ # later on. |
+ if exe.endswith(" (deleted)") and not os.path.isfile(exe): |
+ return "" |
+ return exe |
+ |
+ @wrap_exceptions |
+ def get_process_cmdline(self): |
+ if self.pid == 0: |
+ return [] # special case for kernel process |
+ f = open("/proc/%s/cmdline" % self.pid) |
+ try: |
+ # return the args as a list |
+ return [x for x in f.read().split('\x00') if x] |
+ finally: |
+ f.close() |
+ |
+ @wrap_exceptions |
+ def get_cpu_times(self): |
+ # special case for 0 (kernel process) PID |
+ if self.pid == 0: |
+ return self._cputimes_ntuple(0.0, 0.0) |
+ f = open("/proc/%s/stat" % self.pid) |
+ st = f.read().strip() |
+ f.close() |
+ # ignore the first two values ("pid (exe)") |
+ st = st[st.find(')') + 2:] |
+ values = st.split(' ') |
+ utime = float(values[11]) / _CLOCK_TICKS |
+ stime = float(values[12]) / _CLOCK_TICKS |
+ return self._cputimes_ntuple(utime, stime) |
+ |
+ @wrap_exceptions |
+ def get_process_create_time(self): |
+ # special case for 0 (kernel processes) PID; return system uptime |
+ if self.pid == 0: |
+ return _UPTIME |
+ f = open("/proc/%s/stat" % self.pid) |
+ st = f.read().strip() |
+ f.close() |
+ # ignore the first two values ("pid (exe)") |
+ st = st[st.find(')') + 2:] |
+ values = st.split(' ') |
+ # According to documentation, starttime is in field 21 and the |
+ # unit is jiffies (clock ticks). |
+ # We first divide it for clock ticks and then add uptime returning |
+ # seconds since the epoch, in UTC. |
+ starttime = (float(values[19]) / _CLOCK_TICKS) + _UPTIME |
+ return starttime |
+ |
+ @wrap_exceptions |
+ def get_memory_info(self): |
+ # special case for 0 (kernel processes) PID |
+ if self.pid == 0: |
+ return self._meminfo_ntuple(0, 0) |
+ f = open("/proc/%s/status" % self.pid) |
+ virtual_size = 0 |
+ resident_size = 0 |
+ _flag = False |
+ for line in f: |
+ if (not _flag) and line.startswith("VmSize:"): |
+ virtual_size = int(line.split()[1]) * 1024 |
+ _flag = True |
+ elif line.startswith("VmRSS"): |
+ resident_size = int(line.split()[1]) * 1024 |
+ break |
+ f.close() |
+ return self._meminfo_ntuple(resident_size, virtual_size) |
+ |
+ @wrap_exceptions |
+ def get_process_cwd(self): |
+ if self.pid == 0: |
+ return '' |
+ return os.readlink("/proc/%s/cwd" % self.pid) |
+ |
+ @wrap_exceptions |
+ def get_process_num_threads(self): |
+ if self.pid == 0: |
+ return 0 |
+ f = open("/proc/%s/status" % self.pid) |
+ for line in f: |
+ if line.startswith("Threads:"): |
+ f.close() |
+ return int(line.split()[1]) |
+ |
+ @wrap_exceptions |
+ def get_open_files(self): |
+ retlist = [] |
+ files = os.listdir("/proc/%s/fd" % self.pid) |
+ for fd in files: |
+ file = "/proc/%s/fd/%s" % (self.pid, fd) |
+ if os.path.islink(file): |
+ file = os.readlink(file) |
+ if file.startswith("socket:["): |
+ continue |
+ if file.startswith("pipe:["): |
+ continue |
+ if file == "[]": |
+ continue |
+ if os.path.isfile(file) and not file in retlist: |
+ ntuple = self._openfile_ntuple(file, int(fd)) |
+ retlist.append(ntuple) |
+ return retlist |
+ |
+# --- lsof implementation |
+# |
+# def get_open_files(self): |
+# lsof = _psposix.LsofParser(self.pid, self._process_name) |
+# return lsof.get_process_open_files() |
+ |
+ @wrap_exceptions |
+ def get_connections(self): |
+ if self.pid == 0: |
+ return [] |
+ inodes = {} |
+ # os.listdir() is gonna raise a lot of access denied |
+ # exceptions in case of unprivileged user; that's fine: |
+ # lsof does the same so it's unlikely that we can to better. |
+ for fd in os.listdir("/proc/%s/fd" % self.pid): |
+ try: |
+ inode = os.readlink("/proc/%s/fd/%s" % (self.pid, fd)) |
+ except OSError: |
+ continue |
+ if inode.startswith('socket:['): |
+ # the process is using a socket |
+ inode = inode[8:][:-1] |
+ inodes[inode] = fd |
+ |
+ if not inodes: |
+ # no connections for this process |
+ return [] |
+ |
+ def process(file, family, _type): |
+ retlist = [] |
+ f = open(file) |
+ f.readline() # skip the first line |
+ for line in f: |
+ _, laddr, raddr, status, _, _, _, _, _, inode = line.split()[:10] |
+ if inode in inodes: |
+ laddr = self._decode_address(laddr, family) |
+ raddr = self._decode_address(raddr, family) |
+ if _type == socket.SOCK_STREAM: |
+ status = _TCP_STATES_TABLE[status] |
+ else: |
+ status = "" |
+ fd = int(inodes[inode]) |
+ conn = self._connection_ntuple(fd, family, _type, laddr, |
+ raddr, status) |
+ retlist.append(conn) |
+ f.close() |
+ return retlist |
+ |
+ tcp4 = process("/proc/net/tcp", socket.AF_INET, socket.SOCK_STREAM) |
+ tcp6 = process("/proc/net/tcp6", socket.AF_INET6, socket.SOCK_STREAM) |
+ udp4 = process("/proc/net/udp", socket.AF_INET, socket.SOCK_DGRAM) |
+ udp6 = process("/proc/net/udp6", socket.AF_INET6, socket.SOCK_DGRAM) |
+ return tcp4 + tcp6 + udp4 + udp6 |
+ |
+# --- lsof implementation |
+# |
+# def get_connections(self): |
+# lsof = _psposix.LsofParser(self.pid, self._process_name) |
+# return lsof.get_process_connections() |
+ |
+ @wrap_exceptions |
+ def get_process_ppid(self): |
+ if self.pid == 0: |
+ return 0 |
+ f = open("/proc/%s/status" % self.pid) |
+ for line in f: |
+ if line.startswith("PPid:"): |
+ # PPid: nnnn |
+ f.close() |
+ return int(line.split()[1]) |
+ |
+ @wrap_exceptions |
+ def get_process_uid(self): |
+ if self.pid == 0: |
+ return 0 |
+ f = open("/proc/%s/status" % self.pid) |
+ for line in f: |
+ if line.startswith('Uid:'): |
+ # Uid line provides 4 values which stand for real, |
+ # effective, saved set, and file system UIDs. |
+ # We want to provide real UID only. |
+ f.close() |
+ return int(line.split()[1]) |
+ |
+ @wrap_exceptions |
+ def get_process_gid(self): |
+ if self.pid == 0: |
+ return 0 |
+ f = open("/proc/%s/status" % self.pid) |
+ for line in f: |
+ if line.startswith('Gid:'): |
+ # Uid line provides 4 values which stand for real, |
+ # effective, saved set, and file system GIDs. |
+ # We want to provide real GID only. |
+ f.close() |
+ return int(line.split()[1]) |
+ |
+ @staticmethod |
+ def _decode_address(addr, family): |
+ """Accept an "ip:port" address as displayed in /proc/net/* |
+ and convert it into a human readable form, like: |
+ |
+ "0500000A:0016" -> ("10.0.0.5", 22) |
+ "0000000000000000FFFF00000100007F:9E49" -> ("::ffff:127.0.0.1", 40521) |
+ |
+ The IPv4 address portion is a little-endian four-byte hexadecimal |
+ number; that is, the least significant byte is listed first, |
+ so we need to reverse the order of the bytes to convert it |
+ to an IP address. |
+ The port is represented as a two-byte hexadecimal number. |
+ |
+ Reference: |
+ http://linuxdevcenter.com/pub/a/linux/2000/11/16/LinuxAdmin.html |
+ """ |
+ ip, port = addr.split(':') |
+ port = int(port, 16) |
+ if sys.version_info >= (3,): |
+ ip = ip.encode('ascii') |
+ # this usually refers to a local socket in listen mode with |
+ # no end-points connected |
+ if not port: |
+ return () |
+ if family == socket.AF_INET: |
+ ip = socket.inet_ntop(family, base64.b16decode(ip)[::-1]) |
+ else: # IPv6 |
+ # old version - let's keep it, just in case... |
+ #ip = ip.decode('hex') |
+ #return socket.inet_ntop(socket.AF_INET6, |
+ # ''.join(ip[i:i+4][::-1] for i in xrange(0, 16, 4))) |
+ ip = base64.b16decode(ip) |
+ ip = socket.inet_ntop(socket.AF_INET6, |
+ struct.pack('>4I', *struct.unpack('<4I', ip))) |
+ return (ip, port) |
+ |
+PlatformProcess = LinuxProcess |
+ |