| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # | 2 # |
| 3 # $Id: _pslinux.py 800 2010-11-12 21:51:25Z g.rodola $ | 3 # $Id: _pslinux.py 1142 2011-10-05 18:45:49Z g.rodola $ |
| 4 # | 4 # |
| 5 # Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. |
| 6 # Use of this source code is governed by a BSD-style license that can be |
| 7 # found in the LICENSE file. |
| 5 | 8 |
| 6 __all__ = ["NUM_CPUS", "TOTAL_PHYMEM", | 9 """Linux platform implementation.""" |
| 7 "PlatformProcess", | |
| 8 "avail_phymem", "used_phymem", "total_virtmem", "avail_virtmem", | |
| 9 "used_virtmem", "get_system_cpu_times", "pid_exists", "get_pid_list", | |
| 10 "phymem_buffers", "cached_phymem" | |
| 11 ] | |
| 12 | |
| 13 | 10 |
| 14 import os | 11 import os |
| 15 import errno | 12 import errno |
| 16 import socket | 13 import socket |
| 17 import struct | 14 import struct |
| 18 import sys | 15 import sys |
| 19 import base64 | 16 import base64 |
| 17 import re |
| 20 | 18 |
| 21 try: | 19 import _psutil_posix |
| 22 from collections import namedtuple | 20 import _psutil_linux |
| 23 except ImportError: | 21 from psutil import _psposix |
| 24 from psutil.compat import namedtuple # python < 2.6 | 22 from psutil.error import AccessDenied, NoSuchProcess, TimeoutExpired |
| 23 from psutil._common import * |
| 25 | 24 |
| 26 from psutil import _psposix | 25 __extra__all__ = [ |
| 27 from psutil.error import AccessDenied, NoSuchProcess | 26 "IOPRIO_CLASS_NONE", "IOPRIO_CLASS_RT", "IOPRIO_CLASS_BE", |
| 27 "IOPRIO_CLASS_IDLE", |
| 28 "phymem_buffers", "cached_phymem"] |
| 28 | 29 |
| 29 | 30 |
| 30 def _get_uptime(): | 31 def _get_boot_time(): |
| 31 """Return system boot time (epoch in seconds)""" | 32 """Return system boot time (epoch in seconds)""" |
| 32 f = open('/proc/stat', 'r') | 33 f = open('/proc/stat', 'r') |
| 33 for line in f: | 34 try: |
| 34 if line.startswith('btime'): | 35 for line in f: |
| 35 f.close() | 36 if line.startswith('btime'): |
| 36 return float(line.strip().split()[1]) | 37 return float(line.strip().split()[1]) |
| 38 raise RuntimeError("line not found") |
| 39 finally: |
| 40 f.close() |
| 37 | 41 |
| 38 def _get_num_cpus(): | 42 def _get_num_cpus(): |
| 39 """Return the number of CPUs on the system""" | 43 """Return the number of CPUs on the system""" |
| 40 num = 0 | 44 num = 0 |
| 41 f = open('/proc/cpuinfo', 'r') | 45 f = open('/proc/cpuinfo', 'r') |
| 42 for line in f: | 46 try: |
| 43 if line.startswith('processor'): | 47 lines = f.readlines() |
| 48 finally: |
| 49 f.close() |
| 50 for line in lines: |
| 51 if line.lower().startswith('processor'): |
| 44 num += 1 | 52 num += 1 |
| 45 f.close() | 53 |
| 54 # unknown format (e.g. amrel/sparc architectures), see: |
| 55 # http://code.google.com/p/psutil/issues/detail?id=200 |
| 56 if num == 0: |
| 57 f = open('/proc/stat', 'r') |
| 58 try: |
| 59 lines = f.readlines() |
| 60 finally: |
| 61 f.close() |
| 62 search = re.compile('cpu\d') |
| 63 for line in lines: |
| 64 line = line.split(' ')[0] |
| 65 if search.match(line): |
| 66 num += 1 |
| 67 |
| 68 if num == 0: |
| 69 raise RuntimeError("can't determine number of CPUs") |
| 46 return num | 70 return num |
| 47 | 71 |
| 48 def _get_total_phymem(): | |
| 49 """Return the total amount of physical memory, in bytes""" | |
| 50 f = open('/proc/meminfo', 'r') | |
| 51 for line in f: | |
| 52 if line.startswith('MemTotal:'): | |
| 53 f.close() | |
| 54 return int(line.split()[1]) * 1024 | |
| 55 | |
| 56 | 72 |
| 57 # Number of clock ticks per second | 73 # Number of clock ticks per second |
| 58 _CLOCK_TICKS = os.sysconf(os.sysconf_names["SC_CLK_TCK"]) | 74 _CLOCK_TICKS = os.sysconf(os.sysconf_names["SC_CLK_TCK"]) |
| 59 _UPTIME = _get_uptime() | 75 _TERMINAL_MAP = _psposix._get_terminal_map() |
| 76 BOOT_TIME = _get_boot_time() |
| 60 NUM_CPUS = _get_num_cpus() | 77 NUM_CPUS = _get_num_cpus() |
| 61 TOTAL_PHYMEM = _get_total_phymem() | 78 # ioprio_* constants http://linux.die.net/man/2/ioprio_get |
| 62 | 79 IOPRIO_CLASS_NONE = 0 |
| 63 del _get_uptime, _get_num_cpus, _get_total_phymem | 80 IOPRIO_CLASS_RT = 1 |
| 81 IOPRIO_CLASS_BE = 2 |
| 82 IOPRIO_CLASS_IDLE = 3 |
| 64 | 83 |
| 65 # http://students.mimuw.edu.pl/lxr/source/include/net/tcp_states.h | 84 # http://students.mimuw.edu.pl/lxr/source/include/net/tcp_states.h |
| 66 _TCP_STATES_TABLE = {"01" : "ESTABLISHED", | 85 _TCP_STATES_TABLE = {"01" : "ESTABLISHED", |
| 67 "02" : "SYN_SENT", | 86 "02" : "SYN_SENT", |
| 68 "03" : "SYN_RECV", | 87 "03" : "SYN_RECV", |
| 69 "04" : "FIN_WAIT1", | 88 "04" : "FIN_WAIT1", |
| 70 "05" : "FIN_WAIT2", | 89 "05" : "FIN_WAIT2", |
| 71 "06" : "TIME_WAIT", | 90 "06" : "TIME_WAIT", |
| 72 "07" : "CLOSE", | 91 "07" : "CLOSE", |
| 73 "08" : "CLOSE_WAIT", | 92 "08" : "CLOSE_WAIT", |
| 74 "09" : "LAST_ACK", | 93 "09" : "LAST_ACK", |
| 75 "0A" : "LISTEN", | 94 "0A" : "LISTEN", |
| 76 "0B" : "CLOSING" | 95 "0B" : "CLOSING" |
| 77 } | 96 } |
| 78 | 97 |
| 79 def avail_phymem(): | 98 # --- system memory functions |
| 80 """Return the amount of physical memory available, in bytes.""" | |
| 81 f = open('/proc/meminfo', 'r') | |
| 82 free = None | |
| 83 _flag = False | |
| 84 for line in f: | |
| 85 if line.startswith('MemFree:'): | |
| 86 free = int(line.split()[1]) * 1024 | |
| 87 break | |
| 88 f.close() | |
| 89 return free | |
| 90 | |
| 91 def used_phymem(): | |
| 92 """"Return the amount of physical memory used, in bytes.""" | |
| 93 return (TOTAL_PHYMEM - avail_phymem()) | |
| 94 | |
| 95 def total_virtmem(): | |
| 96 """"Return the total amount of virtual memory, in bytes.""" | |
| 97 f = open('/proc/meminfo', 'r') | |
| 98 for line in f: | |
| 99 if line.startswith('SwapTotal:'): | |
| 100 f.close() | |
| 101 return int(line.split()[1]) * 1024 | |
| 102 | |
| 103 def avail_virtmem(): | |
| 104 """Return the amount of virtual memory currently in use on the | |
| 105 system, in bytes. | |
| 106 """ | |
| 107 f = open('/proc/meminfo', 'r') | |
| 108 for line in f: | |
| 109 if line.startswith('SwapFree:'): | |
| 110 f.close() | |
| 111 return int(line.split()[1]) * 1024 | |
| 112 | |
| 113 def used_virtmem(): | |
| 114 """Return the amount of used memory currently in use on the system, | |
| 115 in bytes. | |
| 116 """ | |
| 117 return total_virtmem() - avail_virtmem() | |
| 118 | 99 |
| 119 def cached_phymem(): | 100 def cached_phymem(): |
| 120 """Return the amount of cached memory on the system, in bytes. | 101 """Return the amount of cached memory on the system, in bytes. |
| 121 This reflects the "cached" column of free command line utility. | 102 This reflects the "cached" column of free command line utility. |
| 122 """ | 103 """ |
| 123 f = open('/proc/meminfo', 'r') | 104 f = open('/proc/meminfo', 'r') |
| 124 for line in f: | 105 try: |
| 125 if line.startswith('Cached:'): | 106 for line in f: |
| 126 f.close() | 107 if line.startswith('Cached:'): |
| 127 return int(line.split()[1]) * 1024 | 108 return int(line.split()[1]) * 1024 |
| 109 raise RuntimeError("line not found") |
| 110 finally: |
| 111 f.close() |
| 128 | 112 |
| 129 def phymem_buffers(): | 113 def phymem_buffers(): |
| 130 """Return the amount of physical memory buffers used by the | 114 """Return the amount of physical memory buffers used by the |
| 131 kernel in bytes. | 115 kernel in bytes. |
| 132 This reflects the "buffers" column of free command line utility. | 116 This reflects the "buffers" column of free command line utility. |
| 133 """ | 117 """ |
| 134 f = open('/proc/meminfo', 'r') | 118 f = open('/proc/meminfo', 'r') |
| 135 for line in f: | 119 try: |
| 136 if line.startswith('Buffers:'): | 120 for line in f: |
| 137 f.close() | 121 if line.startswith('Buffers:'): |
| 138 return int(line.split()[1]) * 1024 | 122 return int(line.split()[1]) * 1024 |
| 123 raise RuntimeError("line not found") |
| 124 finally: |
| 125 f.close() |
| 126 |
| 127 def phymem_usage(): |
| 128 # total, used and free values are matched against free cmdline utility |
| 129 # the percentage matches top/htop and gnome-system-monitor |
| 130 f = open('/proc/meminfo', 'r') |
| 131 try: |
| 132 total = free = buffers = cached = None |
| 133 for line in f: |
| 134 if line.startswith('MemTotal:'): |
| 135 total = int(line.split()[1]) * 1024 |
| 136 elif line.startswith('MemFree:'): |
| 137 free = int(line.split()[1]) * 1024 |
| 138 elif line.startswith('Buffers:'): |
| 139 buffers = int(line.split()[1]) * 1024 |
| 140 elif line.startswith('Cached:'): |
| 141 cached = int(line.split()[1]) * 1024 |
| 142 break |
| 143 used = total - free |
| 144 percent = usage_percent(total - (free + buffers + cached), total, |
| 145 _round=1) |
| 146 return ntuple_sysmeminfo(total, used, free, percent) |
| 147 finally: |
| 148 f.close() |
| 149 |
| 150 |
| 151 def virtmem_usage(): |
| 152 f = open('/proc/meminfo', 'r') |
| 153 try: |
| 154 total = free = None |
| 155 for line in f: |
| 156 if line.startswith('SwapTotal:'): |
| 157 total = int(line.split()[1]) * 1024 |
| 158 elif line.startswith('SwapFree:'): |
| 159 free = int(line.split()[1]) * 1024 |
| 160 if total is not None and free is not None: |
| 161 break |
| 162 assert total is not None and free is not None |
| 163 used = total - free |
| 164 percent = usage_percent(used, total, _round=1) |
| 165 return ntuple_sysmeminfo(total, used, free, percent) |
| 166 finally: |
| 167 f.close() |
| 168 |
| 169 |
| 170 # --- system CPU functions |
| 139 | 171 |
| 140 def get_system_cpu_times(): | 172 def get_system_cpu_times(): |
| 141 """Return a dict representing the following CPU times: | 173 """Return a named tuple representing the following CPU times: |
| 142 user, nice, system, idle, iowait, irq, softirq. | 174 user, nice, system, idle, iowait, irq, softirq. |
| 143 """ | 175 """ |
| 144 f = open('/proc/stat', 'r') | 176 f = open('/proc/stat', 'r') |
| 145 values = f.readline().split() | 177 try: |
| 146 f.close() | 178 values = f.readline().split() |
| 179 finally: |
| 180 f.close() |
| 147 | 181 |
| 148 values = values[1:8] | 182 values = values[1:8] |
| 149 values = tuple([float(x) / _CLOCK_TICKS for x in values]) | 183 values = tuple([float(x) / _CLOCK_TICKS for x in values]) |
| 150 | 184 return ntuple_sys_cputimes(*values[:7]) |
| 151 return dict(user=values[0], nice=values[1], system=values[2], idle=values[3]
, | 185 |
| 152 iowait=values[4], irq=values[5], softirq=values[6]) | 186 def get_system_per_cpu_times(): |
| 187 """Return a list of namedtuple representing the CPU times |
| 188 for every CPU available on the system. |
| 189 """ |
| 190 cpus = [] |
| 191 f = open('/proc/stat', 'r') |
| 192 # get rid of the first line who refers to system wide CPU stats |
| 193 try: |
| 194 f.readline() |
| 195 for line in f.readlines(): |
| 196 if line.startswith('cpu'): |
| 197 values = line.split()[1:8] |
| 198 values = tuple([float(x) / _CLOCK_TICKS for x in values]) |
| 199 entry = ntuple_sys_cputimes(*values[:7]) |
| 200 cpus.append(entry) |
| 201 return cpus |
| 202 finally: |
| 203 f.close() |
| 204 |
| 205 |
| 206 # --- system disk functions |
| 207 |
| 208 def disk_partitions(all=False): |
| 209 """Return mounted disk partitions as a list of nameduples""" |
| 210 phydevs = [] |
| 211 f = open("/proc/filesystems", "r") |
| 212 try: |
| 213 for line in f: |
| 214 if not line.startswith("nodev"): |
| 215 phydevs.append(line.strip()) |
| 216 finally: |
| 217 f.close() |
| 218 |
| 219 retlist = [] |
| 220 partitions = _psutil_linux.get_disk_partitions() |
| 221 for partition in partitions: |
| 222 device, mountpoint, fstype = partition |
| 223 if device == 'none': |
| 224 device = '' |
| 225 if not all: |
| 226 if device == '' or fstype not in phydevs: |
| 227 continue |
| 228 ntuple = ntuple_partition(device, mountpoint, fstype) |
| 229 retlist.append(ntuple) |
| 230 return retlist |
| 231 |
| 232 get_disk_usage = _psposix.get_disk_usage |
| 233 |
| 234 # --- process functions |
| 153 | 235 |
| 154 def get_pid_list(): | 236 def get_pid_list(): |
| 155 """Returns a list of PIDs currently running on the system.""" | 237 """Returns a list of PIDs currently running on the system.""" |
| 156 pids = [int(x) for x in os.listdir('/proc') if x.isdigit()] | 238 pids = [int(x) for x in os.listdir('/proc') if x.isdigit()] |
| 157 # special case for 0 (kernel process) PID | |
| 158 pids.insert(0, 0) | |
| 159 return pids | 239 return pids |
| 160 | 240 |
| 161 def pid_exists(pid): | 241 def pid_exists(pid): |
| 162 """Check For the existence of a unix pid.""" | 242 """Check For the existence of a unix pid.""" |
| 163 return _psposix.pid_exists(pid) | 243 return _psposix.pid_exists(pid) |
| 164 | 244 |
| 245 def network_io_counters(): |
| 246 """Return network I/O statistics for every network interface |
| 247 installed on the system as a dict of raw tuples. |
| 248 """ |
| 249 f = open("/proc/net/dev", "r") |
| 250 try: |
| 251 lines = f.readlines() |
| 252 finally: |
| 253 f.close() |
| 254 |
| 255 retdict = {} |
| 256 for line in lines[2:]: |
| 257 fields = line.split() |
| 258 name = fields[0][:-1] |
| 259 bytes_recv = int(fields[1]) |
| 260 packets_recv = int(fields[2]) |
| 261 bytes_sent = int(fields[9]) |
| 262 packets_sent = int(fields[10]) |
| 263 retdict[name] = (bytes_sent, bytes_recv, packets_sent, packets_recv) |
| 264 return retdict |
| 265 |
| 266 def disk_io_counters(): |
| 267 """Return disk I/O statistics for every disk installed on the |
| 268 system as a dict of raw tuples. |
| 269 """ |
| 270 # man iostat states that sectors are equivalent with blocks and |
| 271 # have a size of 512 bytes since 2.4 kernels. This value is |
| 272 # needed to calculate the amount of disk I/O in bytes. |
| 273 SECTOR_SIZE = 512 |
| 274 |
| 275 # determine partitions we want to look for |
| 276 partitions = [] |
| 277 f = open("/proc/partitions", "r") |
| 278 try: |
| 279 lines = f.readlines()[2:] |
| 280 finally: |
| 281 f.close() |
| 282 for line in lines: |
| 283 _, _, _, name = line.split() |
| 284 if name[-1].isdigit(): |
| 285 partitions.append(name) |
| 286 # |
| 287 retdict = {} |
| 288 f = open("/proc/diskstats", "r") |
| 289 try: |
| 290 lines = f.readlines() |
| 291 finally: |
| 292 f.close() |
| 293 for line in lines: |
| 294 _, _, name, reads, _, rbytes, rtime, writes, _, wbytes, wtime = \ |
| 295 line.split()[:11] |
| 296 if name in partitions: |
| 297 rbytes = int(rbytes) * SECTOR_SIZE |
| 298 wbytes = int(wbytes) * SECTOR_SIZE |
| 299 reads = int(reads) |
| 300 writes = int(writes) |
| 301 # TODO: times are expressed in milliseconds while OSX/BSD has |
| 302 # these expressed in nanoseconds; figure this out. |
| 303 rtime = int(rtime) |
| 304 wtime = int(wtime) |
| 305 retdict[name] = (reads, writes, rbytes, wbytes, rtime, wtime) |
| 306 return retdict |
| 307 |
| 308 |
| 309 # taken from /fs/proc/array.c |
| 310 _status_map = {"R" : STATUS_RUNNING, |
| 311 "S" : STATUS_SLEEPING, |
| 312 "D" : STATUS_DISK_SLEEP, |
| 313 "T" : STATUS_STOPPED, |
| 314 "t" : STATUS_TRACING_STOP, |
| 315 "Z" : STATUS_ZOMBIE, |
| 316 "X" : STATUS_DEAD, |
| 317 "x" : STATUS_DEAD, |
| 318 "K" : STATUS_WAKE_KILL, |
| 319 "W" : STATUS_WAKING} |
| 165 | 320 |
| 166 # --- decorators | 321 # --- decorators |
| 167 | 322 |
| 168 def wrap_exceptions(callable): | 323 def wrap_exceptions(callable): |
| 169 """Call callable into a try/except clause and translate ENOENT, | 324 """Call callable into a try/except clause and translate ENOENT, |
| 170 EACCES and EPERM in NoSuchProcess or AccessDenied exceptions. | 325 EACCES and EPERM in NoSuchProcess or AccessDenied exceptions. |
| 171 """ | 326 """ |
| 172 def wrapper(self, *args, **kwargs): | 327 def wrapper(self, *args, **kwargs): |
| 173 try: | 328 try: |
| 174 return callable(self, *args, **kwargs) | 329 return callable(self, *args, **kwargs) |
| 175 except (OSError, IOError), err: | 330 except (OSError, IOError), err: |
| 176 if err.errno == errno.ENOENT: # no such file or directory | 331 # ENOENT (no such file or directory) gets raised on open(). |
| 332 # ESRCH (no such process) can get raised on read() if |
| 333 # process is gone in meantime. |
| 334 if err.errno in (errno.ENOENT, errno.ESRCH): |
| 177 raise NoSuchProcess(self.pid, self._process_name) | 335 raise NoSuchProcess(self.pid, self._process_name) |
| 178 if err.errno in (errno.EPERM, errno.EACCES): | 336 if err.errno in (errno.EPERM, errno.EACCES): |
| 179 raise AccessDenied(self.pid, self._process_name) | 337 raise AccessDenied(self.pid, self._process_name) |
| 180 raise | 338 raise |
| 181 return wrapper | 339 return wrapper |
| 182 | 340 |
| 183 | 341 |
| 184 class LinuxProcess(object): | 342 class Process(object): |
| 185 """Linux process implementation.""" | 343 """Linux process implementation.""" |
| 186 | 344 |
| 187 _meminfo_ntuple = namedtuple('meminfo', 'rss vms') | |
| 188 _cputimes_ntuple = namedtuple('cputimes', 'user system') | |
| 189 _openfile_ntuple = namedtuple('openfile', 'path fd') | |
| 190 _connection_ntuple = namedtuple('connection', 'fd family type local_address
' | |
| 191 'remote_address status') | |
| 192 __slots__ = ["pid", "_process_name"] | 345 __slots__ = ["pid", "_process_name"] |
| 193 | 346 |
| 194 def __init__(self, pid): | 347 def __init__(self, pid): |
| 195 self.pid = pid | 348 self.pid = pid |
| 196 self._process_name = None | 349 self._process_name = None |
| 197 | 350 |
| 198 @wrap_exceptions | 351 @wrap_exceptions |
| 199 def get_process_name(self): | 352 def get_process_name(self): |
| 200 if self.pid == 0: | |
| 201 return 'sched' # special case for kernel process | |
| 202 f = open("/proc/%s/stat" % self.pid) | 353 f = open("/proc/%s/stat" % self.pid) |
| 203 try: | 354 try: |
| 204 name = f.read().split(' ')[1].replace('(', '').replace(')', '') | 355 name = f.read().split(' ')[1].replace('(', '').replace(')', '') |
| 205 finally: | 356 finally: |
| 206 f.close() | 357 f.close() |
| 207 # XXX - gets changed later and probably needs refactoring | 358 # XXX - gets changed later and probably needs refactoring |
| 208 return name | 359 return name |
| 209 | 360 |
| 210 def get_process_exe(self): | 361 def get_process_exe(self): |
| 211 if self.pid in (0, 2): | 362 if self.pid in (0, 2): |
| 212 return "" # special case for kernel processes | 363 raise AccessDenied(self.pid, self._process_name) |
| 213 try: | 364 try: |
| 214 exe = os.readlink("/proc/%s/exe" % self.pid) | 365 exe = os.readlink("/proc/%s/exe" % self.pid) |
| 215 except (OSError, IOError), err: | 366 except (OSError, IOError), err: |
| 216 if err.errno == errno.ENOENT: | 367 if err.errno == errno.ENOENT: |
| 217 # no such file error; might be raised also if the | 368 # no such file error; might be raised also if the |
| 218 # path actually exists for system processes with | 369 # path actually exists for system processes with |
| 219 # low pids (about 0-20) | 370 # low pids (about 0-20) |
| 220 if os.path.lexists("/proc/%s/exe" % self.pid): | 371 if os.path.lexists("/proc/%s/exe" % self.pid): |
| 221 return "" | 372 return "" |
| 222 else: | 373 else: |
| 223 # ok, it is a process which has gone away | 374 # ok, it is a process which has gone away |
| 224 raise NoSuchProcess(self.pid, self._process_name) | 375 raise NoSuchProcess(self.pid, self._process_name) |
| 225 if err.errno in (errno.EPERM, errno.EACCES): | 376 if err.errno in (errno.EPERM, errno.EACCES): |
| 226 raise AccessDenied(self.pid, self._process_name) | 377 raise AccessDenied(self.pid, self._process_name) |
| 227 raise | 378 raise |
| 228 | 379 |
| 380 # readlink() might return paths containing null bytes causing |
| 381 # problems when used with other fs-related functions (os.*, |
| 382 # open(), ...) |
| 383 exe = exe.replace('\x00', '') |
| 229 # It seems symlinks can point to a deleted/invalid location | 384 # It seems symlinks can point to a deleted/invalid location |
| 230 # (this usually happens with "pulseaudio" process). | 385 # (this usually happens with "pulseaudio" process). |
| 231 # However, if we had permissions to execute readlink() it's | 386 # However, if we had permissions to execute readlink() it's |
| 232 # likely that we'll be able to figure out exe from argv[0] | 387 # likely that we'll be able to figure out exe from argv[0] |
| 233 # later on. | 388 # later on. |
| 234 if exe.endswith(" (deleted)") and not os.path.isfile(exe): | 389 if exe.endswith(" (deleted)") and not os.path.isfile(exe): |
| 235 return "" | 390 return "" |
| 236 return exe | 391 return exe |
| 237 | 392 |
| 238 @wrap_exceptions | 393 @wrap_exceptions |
| 239 def get_process_cmdline(self): | 394 def get_process_cmdline(self): |
| 240 if self.pid == 0: | |
| 241 return [] # special case for kernel process | |
| 242 f = open("/proc/%s/cmdline" % self.pid) | 395 f = open("/proc/%s/cmdline" % self.pid) |
| 243 try: | 396 try: |
| 244 # return the args as a list | 397 # return the args as a list |
| 245 return [x for x in f.read().split('\x00') if x] | 398 return [x for x in f.read().split('\x00') if x] |
| 246 finally: | 399 finally: |
| 247 f.close() | 400 f.close() |
| 248 | 401 |
| 249 @wrap_exceptions | 402 @wrap_exceptions |
| 403 def get_process_terminal(self): |
| 404 f = open("/proc/%s/stat" % self.pid) |
| 405 try: |
| 406 tty_nr = int(f.read().split(' ')[6]) |
| 407 finally: |
| 408 f.close() |
| 409 try: |
| 410 return _TERMINAL_MAP[tty_nr] |
| 411 except KeyError: |
| 412 return None |
| 413 |
| 414 @wrap_exceptions |
| 415 def get_process_io_counters(self): |
| 416 f = open("/proc/%s/io" % self.pid) |
| 417 try: |
| 418 for line in f: |
| 419 if line.startswith("rchar"): |
| 420 read_count = int(line.split()[1]) |
| 421 elif line.startswith("wchar"): |
| 422 write_count = int(line.split()[1]) |
| 423 elif line.startswith("read_bytes"): |
| 424 read_bytes = int(line.split()[1]) |
| 425 elif line.startswith("write_bytes"): |
| 426 write_bytes = int(line.split()[1]) |
| 427 return ntuple_io(read_count, write_count, read_bytes, write_bytes) |
| 428 finally: |
| 429 f.close() |
| 430 |
| 431 @wrap_exceptions |
| 250 def get_cpu_times(self): | 432 def get_cpu_times(self): |
| 251 # special case for 0 (kernel process) PID | |
| 252 if self.pid == 0: | |
| 253 return self._cputimes_ntuple(0.0, 0.0) | |
| 254 f = open("/proc/%s/stat" % self.pid) | 433 f = open("/proc/%s/stat" % self.pid) |
| 255 st = f.read().strip() | 434 try: |
| 256 f.close() | 435 st = f.read().strip() |
| 436 finally: |
| 437 f.close() |
| 257 # ignore the first two values ("pid (exe)") | 438 # ignore the first two values ("pid (exe)") |
| 258 st = st[st.find(')') + 2:] | 439 st = st[st.find(')') + 2:] |
| 259 values = st.split(' ') | 440 values = st.split(' ') |
| 260 utime = float(values[11]) / _CLOCK_TICKS | 441 utime = float(values[11]) / _CLOCK_TICKS |
| 261 stime = float(values[12]) / _CLOCK_TICKS | 442 stime = float(values[12]) / _CLOCK_TICKS |
| 262 return self._cputimes_ntuple(utime, stime) | 443 return ntuple_cputimes(utime, stime) |
| 444 |
| 445 @wrap_exceptions |
| 446 def process_wait(self, timeout=None): |
| 447 try: |
| 448 return _psposix.wait_pid(self.pid, timeout) |
| 449 except TimeoutExpired: |
| 450 raise TimeoutExpired(self.pid, self._process_name) |
| 263 | 451 |
| 264 @wrap_exceptions | 452 @wrap_exceptions |
| 265 def get_process_create_time(self): | 453 def get_process_create_time(self): |
| 266 # special case for 0 (kernel processes) PID; return system uptime | |
| 267 if self.pid == 0: | |
| 268 return _UPTIME | |
| 269 f = open("/proc/%s/stat" % self.pid) | 454 f = open("/proc/%s/stat" % self.pid) |
| 270 st = f.read().strip() | 455 try: |
| 271 f.close() | 456 st = f.read().strip() |
| 457 finally: |
| 458 f.close() |
| 272 # ignore the first two values ("pid (exe)") | 459 # ignore the first two values ("pid (exe)") |
| 273 st = st[st.find(')') + 2:] | 460 st = st[st.find(')') + 2:] |
| 274 values = st.split(' ') | 461 values = st.split(' ') |
| 275 # According to documentation, starttime is in field 21 and the | 462 # According to documentation, starttime is in field 21 and the |
| 276 # unit is jiffies (clock ticks). | 463 # unit is jiffies (clock ticks). |
| 277 # We first divide it for clock ticks and then add uptime returning | 464 # We first divide it for clock ticks and then add uptime returning |
| 278 # seconds since the epoch, in UTC. | 465 # seconds since the epoch, in UTC. |
| 279 starttime = (float(values[19]) / _CLOCK_TICKS) + _UPTIME | 466 starttime = (float(values[19]) / _CLOCK_TICKS) + BOOT_TIME |
| 280 return starttime | 467 return starttime |
| 281 | 468 |
| 282 @wrap_exceptions | 469 @wrap_exceptions |
| 283 def get_memory_info(self): | 470 def get_memory_info(self): |
| 284 # special case for 0 (kernel processes) PID | |
| 285 if self.pid == 0: | |
| 286 return self._meminfo_ntuple(0, 0) | |
| 287 f = open("/proc/%s/status" % self.pid) | 471 f = open("/proc/%s/status" % self.pid) |
| 288 virtual_size = 0 | 472 try: |
| 289 resident_size = 0 | 473 virtual_size = 0 |
| 290 _flag = False | 474 resident_size = 0 |
| 291 for line in f: | 475 _flag = False |
| 292 if (not _flag) and line.startswith("VmSize:"): | 476 for line in f: |
| 293 virtual_size = int(line.split()[1]) * 1024 | 477 if (not _flag) and line.startswith("VmSize:"): |
| 294 _flag = True | 478 virtual_size = int(line.split()[1]) * 1024 |
| 295 elif line.startswith("VmRSS"): | 479 _flag = True |
| 296 resident_size = int(line.split()[1]) * 1024 | 480 elif line.startswith("VmRSS"): |
| 297 break | 481 resident_size = int(line.split()[1]) * 1024 |
| 298 f.close() | 482 break |
| 299 return self._meminfo_ntuple(resident_size, virtual_size) | 483 return ntuple_meminfo(resident_size, virtual_size) |
| 484 finally: |
| 485 f.close() |
| 300 | 486 |
| 301 @wrap_exceptions | 487 @wrap_exceptions |
| 302 def get_process_cwd(self): | 488 def get_process_cwd(self): |
| 303 if self.pid == 0: | 489 # readlink() might return paths containing null bytes causing |
| 304 return '' | 490 # problems when used with other fs-related functions (os.*, |
| 305 return os.readlink("/proc/%s/cwd" % self.pid) | 491 # open(), ...) |
| 492 path = os.readlink("/proc/%s/cwd" % self.pid) |
| 493 return path.replace('\x00', '') |
| 306 | 494 |
| 307 @wrap_exceptions | 495 @wrap_exceptions |
| 308 def get_process_num_threads(self): | 496 def get_process_num_threads(self): |
| 309 if self.pid == 0: | |
| 310 return 0 | |
| 311 f = open("/proc/%s/status" % self.pid) | 497 f = open("/proc/%s/status" % self.pid) |
| 312 for line in f: | 498 try: |
| 313 if line.startswith("Threads:"): | 499 for line in f: |
| 500 if line.startswith("Threads:"): |
| 501 return int(line.split()[1]) |
| 502 raise RuntimeError("line not found") |
| 503 finally: |
| 504 f.close() |
| 505 |
| 506 @wrap_exceptions |
| 507 def get_process_threads(self): |
| 508 thread_ids = os.listdir("/proc/%s/task" % self.pid) |
| 509 thread_ids.sort() |
| 510 retlist = [] |
| 511 for thread_id in thread_ids: |
| 512 try: |
| 513 f = open("/proc/%s/task/%s/stat" % (self.pid, thread_id)) |
| 514 except (OSError, IOError), err: |
| 515 if err.errno == errno.ENOENT: |
| 516 # no such file or directory; it means thread |
| 517 # disappeared on us |
| 518 continue |
| 519 raise |
| 520 try: |
| 521 st = f.read().strip() |
| 522 finally: |
| 314 f.close() | 523 f.close() |
| 315 return int(line.split()[1]) | 524 # ignore the first two values ("pid (exe)") |
| 525 st = st[st.find(')') + 2:] |
| 526 values = st.split(' ') |
| 527 utime = float(values[11]) / _CLOCK_TICKS |
| 528 stime = float(values[12]) / _CLOCK_TICKS |
| 529 ntuple = ntuple_thread(int(thread_id), utime, stime) |
| 530 retlist.append(ntuple) |
| 531 return retlist |
| 532 |
| 533 @wrap_exceptions |
| 534 def get_process_nice(self): |
| 535 #f = open('/proc/%s/stat' % self.pid, 'r') |
| 536 #try: |
| 537 # data = f.read() |
| 538 # return int(data.split()[18]) |
| 539 #finally: |
| 540 # f.close() |
| 541 |
| 542 # Use C implementation |
| 543 return _psutil_posix.getpriority(self.pid) |
| 544 |
| 545 @wrap_exceptions |
| 546 def set_process_nice(self, value): |
| 547 return _psutil_posix.setpriority(self.pid, value) |
| 548 |
| 549 # only starting from kernel 2.6.13 |
| 550 if hasattr(_psutil_linux, "ioprio_get"): |
| 551 |
| 552 @wrap_exceptions |
| 553 def get_process_ionice(self): |
| 554 ioclass, value = _psutil_linux.ioprio_get(self.pid) |
| 555 return ntuple_ionice(ioclass, value) |
| 556 |
| 557 @wrap_exceptions |
| 558 def set_process_ionice(self, ioclass, value): |
| 559 if ioclass in (IOPRIO_CLASS_NONE, None): |
| 560 if value: |
| 561 raise ValueError("can't specify value with IOPRIO_CLASS_NONE
") |
| 562 ioclass = IOPRIO_CLASS_NONE |
| 563 value = 0 |
| 564 if ioclass in (IOPRIO_CLASS_RT, IOPRIO_CLASS_BE): |
| 565 if value is None: |
| 566 value = 4 |
| 567 elif ioclass == IOPRIO_CLASS_IDLE: |
| 568 if value: |
| 569 raise ValueError("can't specify value with IOPRIO_CLASS_IDLE
") |
| 570 value = 0 |
| 571 else: |
| 572 value = 0 |
| 573 if not 0 <= value <= 8: |
| 574 raise ValueError("value argument range expected is between 0 and
8") |
| 575 return _psutil_linux.ioprio_set(self.pid, ioclass, value) |
| 576 |
| 577 @wrap_exceptions |
| 578 def get_process_status(self): |
| 579 f = open("/proc/%s/status" % self.pid) |
| 580 try: |
| 581 for line in f: |
| 582 if line.startswith("State:"): |
| 583 letter = line.split()[1] |
| 584 if letter in _status_map: |
| 585 return _status_map[letter] |
| 586 return constant(-1, '?') |
| 587 finally: |
| 588 f.close() |
| 316 | 589 |
| 317 @wrap_exceptions | 590 @wrap_exceptions |
| 318 def get_open_files(self): | 591 def get_open_files(self): |
| 319 retlist = [] | 592 retlist = [] |
| 320 files = os.listdir("/proc/%s/fd" % self.pid) | 593 files = os.listdir("/proc/%s/fd" % self.pid) |
| 321 for fd in files: | 594 for fd in files: |
| 322 file = "/proc/%s/fd/%s" % (self.pid, fd) | 595 file = "/proc/%s/fd/%s" % (self.pid, fd) |
| 323 if os.path.islink(file): | 596 if os.path.islink(file): |
| 324 file = os.readlink(file) | 597 file = os.readlink(file) |
| 325 if file.startswith("socket:["): | 598 if file.startswith("socket:["): |
| 326 continue | 599 continue |
| 327 if file.startswith("pipe:["): | 600 if file.startswith("pipe:["): |
| 328 continue | 601 continue |
| 329 if file == "[]": | 602 if file == "[]": |
| 330 continue | 603 continue |
| 331 if os.path.isfile(file) and not file in retlist: | 604 if os.path.isfile(file) and not file in retlist: |
| 332 ntuple = self._openfile_ntuple(file, int(fd)) | 605 ntuple = ntuple_openfile(file, int(fd)) |
| 333 retlist.append(ntuple) | 606 retlist.append(ntuple) |
| 334 return retlist | 607 return retlist |
| 335 | 608 |
| 336 # --- lsof implementation | |
| 337 # | |
| 338 # def get_open_files(self): | |
| 339 # lsof = _psposix.LsofParser(self.pid, self._process_name) | |
| 340 # return lsof.get_process_open_files() | |
| 341 | |
| 342 @wrap_exceptions | 609 @wrap_exceptions |
| 343 def get_connections(self): | 610 def get_connections(self): |
| 344 if self.pid == 0: | |
| 345 return [] | |
| 346 inodes = {} | 611 inodes = {} |
| 347 # os.listdir() is gonna raise a lot of access denied | 612 # os.listdir() is gonna raise a lot of access denied |
| 348 # exceptions in case of unprivileged user; that's fine: | 613 # exceptions in case of unprivileged user; that's fine: |
| 349 # lsof does the same so it's unlikely that we can to better. | 614 # lsof does the same so it's unlikely that we can to better. |
| 350 for fd in os.listdir("/proc/%s/fd" % self.pid): | 615 for fd in os.listdir("/proc/%s/fd" % self.pid): |
| 351 try: | 616 try: |
| 352 inode = os.readlink("/proc/%s/fd/%s" % (self.pid, fd)) | 617 inode = os.readlink("/proc/%s/fd/%s" % (self.pid, fd)) |
| 353 except OSError: | 618 except OSError: |
| 354 continue | 619 continue |
| 355 if inode.startswith('socket:['): | 620 if inode.startswith('socket:['): |
| 356 # the process is using a socket | 621 # the process is using a socket |
| 357 inode = inode[8:][:-1] | 622 inode = inode[8:][:-1] |
| 358 inodes[inode] = fd | 623 inodes[inode] = fd |
| 359 | 624 |
| 360 if not inodes: | 625 if not inodes: |
| 361 # no connections for this process | 626 # no connections for this process |
| 362 return [] | 627 return [] |
| 363 | 628 |
| 364 def process(file, family, _type): | 629 def process(file, family, _type): |
| 365 retlist = [] | 630 retlist = [] |
| 366 f = open(file) | 631 f = open(file) |
| 367 f.readline() # skip the first line | 632 try: |
| 368 for line in f: | 633 f.readline() # skip the first line |
| 369 _, laddr, raddr, status, _, _, _, _, _, inode = line.split()[:10
] | 634 for line in f: |
| 370 if inode in inodes: | 635 _, laddr, raddr, status, _, _, _, _, _, inode = \ |
| 371 laddr = self._decode_address(laddr, family) | 636 line.split()[:10] |
| 372 raddr = self._decode_address(raddr, family) | 637 if inode in inodes: |
| 373 if _type == socket.SOCK_STREAM: | 638 laddr = self._decode_address(laddr, family) |
| 374 status = _TCP_STATES_TABLE[status] | 639 raddr = self._decode_address(raddr, family) |
| 375 else: | 640 if _type == socket.SOCK_STREAM: |
| 376 status = "" | 641 status = _TCP_STATES_TABLE[status] |
| 377 fd = int(inodes[inode]) | 642 else: |
| 378 conn = self._connection_ntuple(fd, family, _type, laddr, | 643 status = "" |
| 379 raddr, status) | 644 fd = int(inodes[inode]) |
| 380 retlist.append(conn) | 645 conn = ntuple_connection(fd, family, _type, laddr, |
| 381 f.close() | 646 raddr, status) |
| 382 return retlist | 647 retlist.append(conn) |
| 648 return retlist |
| 649 finally: |
| 650 f.close() |
| 383 | 651 |
| 384 tcp4 = process("/proc/net/tcp", socket.AF_INET, socket.SOCK_STREAM) | 652 tcp4 = process("/proc/net/tcp", socket.AF_INET, socket.SOCK_STREAM) |
| 385 tcp6 = process("/proc/net/tcp6", socket.AF_INET6, socket.SOCK_STREAM) | |
| 386 udp4 = process("/proc/net/udp", socket.AF_INET, socket.SOCK_DGRAM) | 653 udp4 = process("/proc/net/udp", socket.AF_INET, socket.SOCK_DGRAM) |
| 387 udp6 = process("/proc/net/udp6", socket.AF_INET6, socket.SOCK_DGRAM) | 654 try: |
| 655 tcp6 = process("/proc/net/tcp6", socket.AF_INET6, socket.SOCK_STREAM
) |
| 656 udp6 = process("/proc/net/udp6", socket.AF_INET6, socket.SOCK_DGRAM) |
| 657 except IOError, err: |
| 658 if err.errno == errno.ENOENT: |
| 659 # IPv6 is not supported on this platform |
| 660 tcp6 = udp6 = [] |
| 661 else: |
| 662 raise |
| 388 return tcp4 + tcp6 + udp4 + udp6 | 663 return tcp4 + tcp6 + udp4 + udp6 |
| 389 | 664 |
| 390 # --- lsof implementation | 665 # --- lsof implementation |
| 391 # | 666 # |
| 392 # def get_connections(self): | 667 # def get_connections(self): |
| 393 # lsof = _psposix.LsofParser(self.pid, self._process_name) | 668 # lsof = _psposix.LsofParser(self.pid, self._process_name) |
| 394 # return lsof.get_process_connections() | 669 # return lsof.get_process_connections() |
| 395 | 670 |
| 396 @wrap_exceptions | 671 @wrap_exceptions |
| 397 def get_process_ppid(self): | 672 def get_process_ppid(self): |
| 398 if self.pid == 0: | |
| 399 return 0 | |
| 400 f = open("/proc/%s/status" % self.pid) | 673 f = open("/proc/%s/status" % self.pid) |
| 401 for line in f: | 674 try: |
| 402 if line.startswith("PPid:"): | 675 for line in f: |
| 403 # PPid: nnnn | 676 if line.startswith("PPid:"): |
| 404 f.close() | 677 # PPid: nnnn |
| 405 return int(line.split()[1]) | 678 return int(line.split()[1]) |
| 679 raise RuntimeError("line not found") |
| 680 finally: |
| 681 f.close() |
| 406 | 682 |
| 407 @wrap_exceptions | 683 @wrap_exceptions |
| 408 def get_process_uid(self): | 684 def get_process_uids(self): |
| 409 if self.pid == 0: | |
| 410 return 0 | |
| 411 f = open("/proc/%s/status" % self.pid) | 685 f = open("/proc/%s/status" % self.pid) |
| 412 for line in f: | 686 try: |
| 413 if line.startswith('Uid:'): | 687 for line in f: |
| 414 # Uid line provides 4 values which stand for real, | 688 if line.startswith('Uid:'): |
| 415 # effective, saved set, and file system UIDs. | 689 _, real, effective, saved, fs = line.split() |
| 416 # We want to provide real UID only. | 690 return ntuple_uids(int(real), int(effective), int(saved)) |
| 417 f.close() | 691 raise RuntimeError("line not found") |
| 418 return int(line.split()[1]) | 692 finally: |
| 693 f.close() |
| 419 | 694 |
| 420 @wrap_exceptions | 695 @wrap_exceptions |
| 421 def get_process_gid(self): | 696 def get_process_gids(self): |
| 422 if self.pid == 0: | |
| 423 return 0 | |
| 424 f = open("/proc/%s/status" % self.pid) | 697 f = open("/proc/%s/status" % self.pid) |
| 425 for line in f: | 698 try: |
| 426 if line.startswith('Gid:'): | 699 for line in f: |
| 427 # Uid line provides 4 values which stand for real, | 700 if line.startswith('Gid:'): |
| 428 # effective, saved set, and file system GIDs. | 701 _, real, effective, saved, fs = line.split() |
| 429 # We want to provide real GID only. | 702 return ntuple_gids(int(real), int(effective), int(saved)) |
| 430 f.close() | 703 raise RuntimeError("line not found") |
| 431 return int(line.split()[1]) | 704 finally: |
| 705 f.close() |
| 432 | 706 |
| 433 @staticmethod | 707 @staticmethod |
| 434 def _decode_address(addr, family): | 708 def _decode_address(addr, family): |
| 435 """Accept an "ip:port" address as displayed in /proc/net/* | 709 """Accept an "ip:port" address as displayed in /proc/net/* |
| 436 and convert it into a human readable form, like: | 710 and convert it into a human readable form, like: |
| 437 | 711 |
| 438 "0500000A:0016" -> ("10.0.0.5", 22) | 712 "0500000A:0016" -> ("10.0.0.5", 22) |
| 439 "0000000000000000FFFF00000100007F:9E49" -> ("::ffff:127.0.0.1", 40521) | 713 "0000000000000000FFFF00000100007F:9E49" -> ("::ffff:127.0.0.1", 40521) |
| 440 | 714 |
| 441 The IPv4 address portion is a little-endian four-byte hexadecimal | 715 The IP address portion is a little or big endian four-byte |
| 442 number; that is, the least significant byte is listed first, | 716 hexadecimal number; that is, the least significant byte is listed |
| 443 so we need to reverse the order of the bytes to convert it | 717 first, so we need to reverse the order of the bytes to convert it |
| 444 to an IP address. | 718 to an IP address. |
| 445 The port is represented as a two-byte hexadecimal number. | 719 The port is represented as a two-byte hexadecimal number. |
| 446 | 720 |
| 447 Reference: | 721 Reference: |
| 448 http://linuxdevcenter.com/pub/a/linux/2000/11/16/LinuxAdmin.html | 722 http://linuxdevcenter.com/pub/a/linux/2000/11/16/LinuxAdmin.html |
| 449 """ | 723 """ |
| 450 ip, port = addr.split(':') | 724 ip, port = addr.split(':') |
| 451 port = int(port, 16) | 725 port = int(port, 16) |
| 452 if sys.version_info >= (3,): | 726 if sys.version_info >= (3,): |
| 453 ip = ip.encode('ascii') | 727 ip = ip.encode('ascii') |
| 454 # this usually refers to a local socket in listen mode with | 728 # this usually refers to a local socket in listen mode with |
| 455 # no end-points connected | 729 # no end-points connected |
| 456 if not port: | 730 if not port: |
| 457 return () | 731 return () |
| 458 if family == socket.AF_INET: | 732 if family == socket.AF_INET: |
| 459 ip = socket.inet_ntop(family, base64.b16decode(ip)[::-1]) | 733 # see: http://code.google.com/p/psutil/issues/detail?id=201 |
| 734 if sys.byteorder == 'little': |
| 735 ip = socket.inet_ntop(family, base64.b16decode(ip)[::-1]) |
| 736 else: |
| 737 ip = socket.inet_ntop(family, base64.b16decode(ip)) |
| 460 else: # IPv6 | 738 else: # IPv6 |
| 461 # old version - let's keep it, just in case... | 739 # old version - let's keep it, just in case... |
| 462 #ip = ip.decode('hex') | 740 #ip = ip.decode('hex') |
| 463 #return socket.inet_ntop(socket.AF_INET6, | 741 #return socket.inet_ntop(socket.AF_INET6, |
| 464 # ''.join(ip[i:i+4][::-1] for i in xrange(0, 16, 4))) | 742 # ''.join(ip[i:i+4][::-1] for i in xrange(0, 16, 4))) |
| 465 ip = base64.b16decode(ip) | 743 ip = base64.b16decode(ip) |
| 466 ip = socket.inet_ntop(socket.AF_INET6, | 744 # see: http://code.google.com/p/psutil/issues/detail?id=201 |
| 745 if sys.byteorder == 'little': |
| 746 ip = socket.inet_ntop(socket.AF_INET6, |
| 467 struct.pack('>4I', *struct.unpack('<4I', ip))) | 747 struct.pack('>4I', *struct.unpack('<4I', ip))) |
| 748 else: |
| 749 ip = socket.inet_ntop(socket.AF_INET6, |
| 750 struct.pack('<4I', *struct.unpack('<4I', ip))) |
| 468 return (ip, port) | 751 return (ip, port) |
| 469 | |
| 470 PlatformProcess = LinuxProcess | |
| 471 | |
| OLD | NEW |