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 |