comparison env/lib/python3.9/site-packages/psutil/_pswindows.py @ 0:4f3585e2f14b draft default tip

"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author shellac
date Mon, 22 Mar 2021 18:12:50 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:4f3585e2f14b
1 # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 """Windows platform implementation."""
6
7 import contextlib
8 import errno
9 import functools
10 import os
11 import signal
12 import sys
13 import time
14 from collections import namedtuple
15
16 from . import _common
17 from ._common import AccessDenied
18 from ._common import conn_tmap
19 from ._common import conn_to_ntuple
20 from ._common import debug
21 from ._common import ENCODING
22 from ._common import ENCODING_ERRS
23 from ._common import isfile_strict
24 from ._common import memoize
25 from ._common import memoize_when_activated
26 from ._common import NoSuchProcess
27 from ._common import parse_environ_block
28 from ._common import TimeoutExpired
29 from ._common import usage_percent
30 from ._compat import long
31 from ._compat import lru_cache
32 from ._compat import PY3
33 from ._compat import range
34 from ._compat import unicode
35 from ._psutil_windows import ABOVE_NORMAL_PRIORITY_CLASS
36 from ._psutil_windows import BELOW_NORMAL_PRIORITY_CLASS
37 from ._psutil_windows import HIGH_PRIORITY_CLASS
38 from ._psutil_windows import IDLE_PRIORITY_CLASS
39 from ._psutil_windows import NORMAL_PRIORITY_CLASS
40 from ._psutil_windows import REALTIME_PRIORITY_CLASS
41
42 try:
43 from . import _psutil_windows as cext
44 except ImportError as err:
45 if str(err).lower().startswith("dll load failed") and \
46 sys.getwindowsversion()[0] < 6:
47 # We may get here if:
48 # 1) we are on an old Windows version
49 # 2) psutil was installed via pip + wheel
50 # See: https://github.com/giampaolo/psutil/issues/811
51 msg = "this Windows version is too old (< Windows Vista); "
52 msg += "psutil 3.4.2 is the latest version which supports Windows "
53 msg += "2000, XP and 2003 server"
54 raise RuntimeError(msg)
55 else:
56 raise
57
58 if sys.version_info >= (3, 4):
59 import enum
60 else:
61 enum = None
62
63 # process priority constants, import from __init__.py:
64 # http://msdn.microsoft.com/en-us/library/ms686219(v=vs.85).aspx
65 __extra__all__ = [
66 "win_service_iter", "win_service_get",
67 # Process priority
68 "ABOVE_NORMAL_PRIORITY_CLASS", "BELOW_NORMAL_PRIORITY_CLASS",
69 "HIGH_PRIORITY_CLASS", "IDLE_PRIORITY_CLASS", "NORMAL_PRIORITY_CLASS",
70 "REALTIME_PRIORITY_CLASS",
71 # IO priority
72 "IOPRIO_VERYLOW", "IOPRIO_LOW", "IOPRIO_NORMAL", "IOPRIO_HIGH",
73 # others
74 "CONN_DELETE_TCB", "AF_LINK",
75 ]
76
77
78 # =====================================================================
79 # --- globals
80 # =====================================================================
81
82 CONN_DELETE_TCB = "DELETE_TCB"
83 ERROR_PARTIAL_COPY = 299
84 PYPY = '__pypy__' in sys.builtin_module_names
85
86 if enum is None:
87 AF_LINK = -1
88 else:
89 AddressFamily = enum.IntEnum('AddressFamily', {'AF_LINK': -1})
90 AF_LINK = AddressFamily.AF_LINK
91
92 TCP_STATUSES = {
93 cext.MIB_TCP_STATE_ESTAB: _common.CONN_ESTABLISHED,
94 cext.MIB_TCP_STATE_SYN_SENT: _common.CONN_SYN_SENT,
95 cext.MIB_TCP_STATE_SYN_RCVD: _common.CONN_SYN_RECV,
96 cext.MIB_TCP_STATE_FIN_WAIT1: _common.CONN_FIN_WAIT1,
97 cext.MIB_TCP_STATE_FIN_WAIT2: _common.CONN_FIN_WAIT2,
98 cext.MIB_TCP_STATE_TIME_WAIT: _common.CONN_TIME_WAIT,
99 cext.MIB_TCP_STATE_CLOSED: _common.CONN_CLOSE,
100 cext.MIB_TCP_STATE_CLOSE_WAIT: _common.CONN_CLOSE_WAIT,
101 cext.MIB_TCP_STATE_LAST_ACK: _common.CONN_LAST_ACK,
102 cext.MIB_TCP_STATE_LISTEN: _common.CONN_LISTEN,
103 cext.MIB_TCP_STATE_CLOSING: _common.CONN_CLOSING,
104 cext.MIB_TCP_STATE_DELETE_TCB: CONN_DELETE_TCB,
105 cext.PSUTIL_CONN_NONE: _common.CONN_NONE,
106 }
107
108 if enum is not None:
109 class Priority(enum.IntEnum):
110 ABOVE_NORMAL_PRIORITY_CLASS = ABOVE_NORMAL_PRIORITY_CLASS
111 BELOW_NORMAL_PRIORITY_CLASS = BELOW_NORMAL_PRIORITY_CLASS
112 HIGH_PRIORITY_CLASS = HIGH_PRIORITY_CLASS
113 IDLE_PRIORITY_CLASS = IDLE_PRIORITY_CLASS
114 NORMAL_PRIORITY_CLASS = NORMAL_PRIORITY_CLASS
115 REALTIME_PRIORITY_CLASS = REALTIME_PRIORITY_CLASS
116
117 globals().update(Priority.__members__)
118
119 if enum is None:
120 IOPRIO_VERYLOW = 0
121 IOPRIO_LOW = 1
122 IOPRIO_NORMAL = 2
123 IOPRIO_HIGH = 3
124 else:
125 class IOPriority(enum.IntEnum):
126 IOPRIO_VERYLOW = 0
127 IOPRIO_LOW = 1
128 IOPRIO_NORMAL = 2
129 IOPRIO_HIGH = 3
130 globals().update(IOPriority.__members__)
131
132 pinfo_map = dict(
133 num_handles=0,
134 ctx_switches=1,
135 user_time=2,
136 kernel_time=3,
137 create_time=4,
138 num_threads=5,
139 io_rcount=6,
140 io_wcount=7,
141 io_rbytes=8,
142 io_wbytes=9,
143 io_count_others=10,
144 io_bytes_others=11,
145 num_page_faults=12,
146 peak_wset=13,
147 wset=14,
148 peak_paged_pool=15,
149 paged_pool=16,
150 peak_non_paged_pool=17,
151 non_paged_pool=18,
152 pagefile=19,
153 peak_pagefile=20,
154 mem_private=21,
155 )
156
157
158 # =====================================================================
159 # --- named tuples
160 # =====================================================================
161
162
163 # psutil.cpu_times()
164 scputimes = namedtuple('scputimes',
165 ['user', 'system', 'idle', 'interrupt', 'dpc'])
166 # psutil.virtual_memory()
167 svmem = namedtuple('svmem', ['total', 'available', 'percent', 'used', 'free'])
168 # psutil.Process.memory_info()
169 pmem = namedtuple(
170 'pmem', ['rss', 'vms',
171 'num_page_faults', 'peak_wset', 'wset', 'peak_paged_pool',
172 'paged_pool', 'peak_nonpaged_pool', 'nonpaged_pool',
173 'pagefile', 'peak_pagefile', 'private'])
174 # psutil.Process.memory_full_info()
175 pfullmem = namedtuple('pfullmem', pmem._fields + ('uss', ))
176 # psutil.Process.memory_maps(grouped=True)
177 pmmap_grouped = namedtuple('pmmap_grouped', ['path', 'rss'])
178 # psutil.Process.memory_maps(grouped=False)
179 pmmap_ext = namedtuple(
180 'pmmap_ext', 'addr perms ' + ' '.join(pmmap_grouped._fields))
181 # psutil.Process.io_counters()
182 pio = namedtuple('pio', ['read_count', 'write_count',
183 'read_bytes', 'write_bytes',
184 'other_count', 'other_bytes'])
185
186
187 # =====================================================================
188 # --- utils
189 # =====================================================================
190
191
192 @lru_cache(maxsize=512)
193 def convert_dos_path(s):
194 r"""Convert paths using native DOS format like:
195 "\Device\HarddiskVolume1\Windows\systemew\file.txt"
196 into:
197 "C:\Windows\systemew\file.txt"
198 """
199 rawdrive = '\\'.join(s.split('\\')[:3])
200 driveletter = cext.win32_QueryDosDevice(rawdrive)
201 remainder = s[len(rawdrive):]
202 return os.path.join(driveletter, remainder)
203
204
205 def py2_strencode(s):
206 """Encode a unicode string to a byte string by using the default fs
207 encoding + "replace" error handler.
208 """
209 if PY3:
210 return s
211 else:
212 if isinstance(s, str):
213 return s
214 else:
215 return s.encode(ENCODING, ENCODING_ERRS)
216
217
218 @memoize
219 def getpagesize():
220 return cext.getpagesize()
221
222
223 # =====================================================================
224 # --- memory
225 # =====================================================================
226
227
228 def virtual_memory():
229 """System virtual memory as a namedtuple."""
230 mem = cext.virtual_mem()
231 totphys, availphys, totpagef, availpagef, totvirt, freevirt = mem
232 #
233 total = totphys
234 avail = availphys
235 free = availphys
236 used = total - avail
237 percent = usage_percent((total - avail), total, round_=1)
238 return svmem(total, avail, percent, used, free)
239
240
241 def swap_memory():
242 """Swap system memory as a (total, used, free, sin, sout) tuple."""
243 mem = cext.virtual_mem()
244 total = mem[2]
245 free = mem[3]
246 used = total - free
247 percent = usage_percent(used, total, round_=1)
248 return _common.sswap(total, used, free, percent, 0, 0)
249
250
251 # =====================================================================
252 # --- disk
253 # =====================================================================
254
255
256 disk_io_counters = cext.disk_io_counters
257
258
259 def disk_usage(path):
260 """Return disk usage associated with path."""
261 if PY3 and isinstance(path, bytes):
262 # XXX: do we want to use "strict"? Probably yes, in order
263 # to fail immediately. After all we are accepting input here...
264 path = path.decode(ENCODING, errors="strict")
265 total, free = cext.disk_usage(path)
266 used = total - free
267 percent = usage_percent(used, total, round_=1)
268 return _common.sdiskusage(total, used, free, percent)
269
270
271 def disk_partitions(all):
272 """Return disk partitions."""
273 rawlist = cext.disk_partitions(all)
274 return [_common.sdiskpart(*x) for x in rawlist]
275
276
277 # =====================================================================
278 # --- CPU
279 # =====================================================================
280
281
282 def cpu_times():
283 """Return system CPU times as a named tuple."""
284 user, system, idle = cext.cpu_times()
285 # Internally, GetSystemTimes() is used, and it doesn't return
286 # interrupt and dpc times. cext.per_cpu_times() does, so we
287 # rely on it to get those only.
288 percpu_summed = scputimes(*[sum(n) for n in zip(*cext.per_cpu_times())])
289 return scputimes(user, system, idle,
290 percpu_summed.interrupt, percpu_summed.dpc)
291
292
293 def per_cpu_times():
294 """Return system per-CPU times as a list of named tuples."""
295 ret = []
296 for user, system, idle, interrupt, dpc in cext.per_cpu_times():
297 item = scputimes(user, system, idle, interrupt, dpc)
298 ret.append(item)
299 return ret
300
301
302 def cpu_count_logical():
303 """Return the number of logical CPUs in the system."""
304 return cext.cpu_count_logical()
305
306
307 def cpu_count_physical():
308 """Return the number of physical CPU cores in the system."""
309 return cext.cpu_count_phys()
310
311
312 def cpu_stats():
313 """Return CPU statistics."""
314 ctx_switches, interrupts, dpcs, syscalls = cext.cpu_stats()
315 soft_interrupts = 0
316 return _common.scpustats(ctx_switches, interrupts, soft_interrupts,
317 syscalls)
318
319
320 def cpu_freq():
321 """Return CPU frequency.
322 On Windows per-cpu frequency is not supported.
323 """
324 curr, max_ = cext.cpu_freq()
325 min_ = 0.0
326 return [_common.scpufreq(float(curr), min_, float(max_))]
327
328
329 _loadavg_inititialized = False
330
331
332 def getloadavg():
333 """Return the number of processes in the system run queue averaged
334 over the last 1, 5, and 15 minutes respectively as a tuple"""
335 global _loadavg_inititialized
336
337 if not _loadavg_inititialized:
338 cext.init_loadavg_counter()
339 _loadavg_inititialized = True
340
341 # Drop to 2 decimal points which is what Linux does
342 raw_loads = cext.getloadavg()
343 return tuple([round(load, 2) for load in raw_loads])
344
345
346 # =====================================================================
347 # --- network
348 # =====================================================================
349
350
351 def net_connections(kind, _pid=-1):
352 """Return socket connections. If pid == -1 return system-wide
353 connections (as opposed to connections opened by one process only).
354 """
355 if kind not in conn_tmap:
356 raise ValueError("invalid %r kind argument; choose between %s"
357 % (kind, ', '.join([repr(x) for x in conn_tmap])))
358 families, types = conn_tmap[kind]
359 rawlist = cext.net_connections(_pid, families, types)
360 ret = set()
361 for item in rawlist:
362 fd, fam, type, laddr, raddr, status, pid = item
363 nt = conn_to_ntuple(fd, fam, type, laddr, raddr, status, TCP_STATUSES,
364 pid=pid if _pid == -1 else None)
365 ret.add(nt)
366 return list(ret)
367
368
369 def net_if_stats():
370 """Get NIC stats (isup, duplex, speed, mtu)."""
371 ret = {}
372 rawdict = cext.net_if_stats()
373 for name, items in rawdict.items():
374 if not PY3:
375 assert isinstance(name, unicode), type(name)
376 name = py2_strencode(name)
377 isup, duplex, speed, mtu = items
378 if hasattr(_common, 'NicDuplex'):
379 duplex = _common.NicDuplex(duplex)
380 ret[name] = _common.snicstats(isup, duplex, speed, mtu)
381 return ret
382
383
384 def net_io_counters():
385 """Return network I/O statistics for every network interface
386 installed on the system as a dict of raw tuples.
387 """
388 ret = cext.net_io_counters()
389 return dict([(py2_strencode(k), v) for k, v in ret.items()])
390
391
392 def net_if_addrs():
393 """Return the addresses associated to each NIC."""
394 ret = []
395 for items in cext.net_if_addrs():
396 items = list(items)
397 items[0] = py2_strencode(items[0])
398 ret.append(items)
399 return ret
400
401
402 # =====================================================================
403 # --- sensors
404 # =====================================================================
405
406
407 def sensors_battery():
408 """Return battery information."""
409 # For constants meaning see:
410 # https://msdn.microsoft.com/en-us/library/windows/desktop/
411 # aa373232(v=vs.85).aspx
412 acline_status, flags, percent, secsleft = cext.sensors_battery()
413 power_plugged = acline_status == 1
414 no_battery = bool(flags & 128)
415 charging = bool(flags & 8)
416
417 if no_battery:
418 return None
419 if power_plugged or charging:
420 secsleft = _common.POWER_TIME_UNLIMITED
421 elif secsleft == -1:
422 secsleft = _common.POWER_TIME_UNKNOWN
423
424 return _common.sbattery(percent, secsleft, power_plugged)
425
426
427 # =====================================================================
428 # --- other system functions
429 # =====================================================================
430
431
432 _last_btime = 0
433
434
435 def boot_time():
436 """The system boot time expressed in seconds since the epoch."""
437 # This dirty hack is to adjust the precision of the returned
438 # value which may have a 1 second fluctuation, see:
439 # https://github.com/giampaolo/psutil/issues/1007
440 global _last_btime
441 ret = float(cext.boot_time())
442 if abs(ret - _last_btime) <= 1:
443 return _last_btime
444 else:
445 _last_btime = ret
446 return ret
447
448
449 def users():
450 """Return currently connected users as a list of namedtuples."""
451 retlist = []
452 rawlist = cext.users()
453 for item in rawlist:
454 user, hostname, tstamp = item
455 user = py2_strencode(user)
456 nt = _common.suser(user, None, hostname, tstamp, None)
457 retlist.append(nt)
458 return retlist
459
460
461 # =====================================================================
462 # --- Windows services
463 # =====================================================================
464
465
466 def win_service_iter():
467 """Yields a list of WindowsService instances."""
468 for name, display_name in cext.winservice_enumerate():
469 yield WindowsService(py2_strencode(name), py2_strencode(display_name))
470
471
472 def win_service_get(name):
473 """Open a Windows service and return it as a WindowsService instance."""
474 service = WindowsService(name, None)
475 service._display_name = service._query_config()['display_name']
476 return service
477
478
479 class WindowsService(object):
480 """Represents an installed Windows service."""
481
482 def __init__(self, name, display_name):
483 self._name = name
484 self._display_name = display_name
485
486 def __str__(self):
487 details = "(name=%r, display_name=%r)" % (
488 self._name, self._display_name)
489 return "%s%s" % (self.__class__.__name__, details)
490
491 def __repr__(self):
492 return "<%s at %s>" % (self.__str__(), id(self))
493
494 def __eq__(self, other):
495 # Test for equality with another WindosService object based
496 # on name.
497 if not isinstance(other, WindowsService):
498 return NotImplemented
499 return self._name == other._name
500
501 def __ne__(self, other):
502 return not self == other
503
504 def _query_config(self):
505 with self._wrap_exceptions():
506 display_name, binpath, username, start_type = \
507 cext.winservice_query_config(self._name)
508 # XXX - update _self.display_name?
509 return dict(
510 display_name=py2_strencode(display_name),
511 binpath=py2_strencode(binpath),
512 username=py2_strencode(username),
513 start_type=py2_strencode(start_type))
514
515 def _query_status(self):
516 with self._wrap_exceptions():
517 status, pid = cext.winservice_query_status(self._name)
518 if pid == 0:
519 pid = None
520 return dict(status=status, pid=pid)
521
522 @contextlib.contextmanager
523 def _wrap_exceptions(self):
524 """Ctx manager which translates bare OSError and WindowsError
525 exceptions into NoSuchProcess and AccessDenied.
526 """
527 try:
528 yield
529 except OSError as err:
530 if is_permission_err(err):
531 raise AccessDenied(
532 pid=None, name=self._name,
533 msg="service %r is not querable (not enough privileges)" %
534 self._name)
535 elif err.winerror in (cext.ERROR_INVALID_NAME,
536 cext.ERROR_SERVICE_DOES_NOT_EXIST):
537 raise NoSuchProcess(
538 pid=None, name=self._name,
539 msg="service %r does not exist)" % self._name)
540 else:
541 raise
542
543 # config query
544
545 def name(self):
546 """The service name. This string is how a service is referenced
547 and can be passed to win_service_get() to get a new
548 WindowsService instance.
549 """
550 return self._name
551
552 def display_name(self):
553 """The service display name. The value is cached when this class
554 is instantiated.
555 """
556 return self._display_name
557
558 def binpath(self):
559 """The fully qualified path to the service binary/exe file as
560 a string, including command line arguments.
561 """
562 return self._query_config()['binpath']
563
564 def username(self):
565 """The name of the user that owns this service."""
566 return self._query_config()['username']
567
568 def start_type(self):
569 """A string which can either be "automatic", "manual" or
570 "disabled".
571 """
572 return self._query_config()['start_type']
573
574 # status query
575
576 def pid(self):
577 """The process PID, if any, else None. This can be passed
578 to Process class to control the service's process.
579 """
580 return self._query_status()['pid']
581
582 def status(self):
583 """Service status as a string."""
584 return self._query_status()['status']
585
586 def description(self):
587 """Service long description."""
588 return py2_strencode(cext.winservice_query_descr(self.name()))
589
590 # utils
591
592 def as_dict(self):
593 """Utility method retrieving all the information above as a
594 dictionary.
595 """
596 d = self._query_config()
597 d.update(self._query_status())
598 d['name'] = self.name()
599 d['display_name'] = self.display_name()
600 d['description'] = self.description()
601 return d
602
603 # actions
604 # XXX: the necessary C bindings for start() and stop() are
605 # implemented but for now I prefer not to expose them.
606 # I may change my mind in the future. Reasons:
607 # - they require Administrator privileges
608 # - can't implement a timeout for stop() (unless by using a thread,
609 # which sucks)
610 # - would require adding ServiceAlreadyStarted and
611 # ServiceAlreadyStopped exceptions, adding two new APIs.
612 # - we might also want to have modify(), which would basically mean
613 # rewriting win32serviceutil.ChangeServiceConfig, which involves a
614 # lot of stuff (and API constants which would pollute the API), see:
615 # http://pyxr.sourceforge.net/PyXR/c/python24/lib/site-packages/
616 # win32/lib/win32serviceutil.py.html#0175
617 # - psutil is typically about "read only" monitoring stuff;
618 # win_service_* APIs should only be used to retrieve a service and
619 # check whether it's running
620
621 # def start(self, timeout=None):
622 # with self._wrap_exceptions():
623 # cext.winservice_start(self.name())
624 # if timeout:
625 # giveup_at = time.time() + timeout
626 # while True:
627 # if self.status() == "running":
628 # return
629 # else:
630 # if time.time() > giveup_at:
631 # raise TimeoutExpired(timeout)
632 # else:
633 # time.sleep(.1)
634
635 # def stop(self):
636 # # Note: timeout is not implemented because it's just not
637 # # possible, see:
638 # # http://stackoverflow.com/questions/11973228/
639 # with self._wrap_exceptions():
640 # return cext.winservice_stop(self.name())
641
642
643 # =====================================================================
644 # --- processes
645 # =====================================================================
646
647
648 pids = cext.pids
649 pid_exists = cext.pid_exists
650 ppid_map = cext.ppid_map # used internally by Process.children()
651
652
653 def is_permission_err(exc):
654 """Return True if this is a permission error."""
655 assert isinstance(exc, OSError), exc
656 # On Python 2 OSError doesn't always have 'winerror'. Sometimes
657 # it does, in which case the original exception was WindowsError
658 # (which is a subclass of OSError).
659 return exc.errno in (errno.EPERM, errno.EACCES) or \
660 getattr(exc, "winerror", -1) in (cext.ERROR_ACCESS_DENIED,
661 cext.ERROR_PRIVILEGE_NOT_HELD)
662
663
664 def convert_oserror(exc, pid=None, name=None):
665 """Convert OSError into NoSuchProcess or AccessDenied."""
666 assert isinstance(exc, OSError), exc
667 if is_permission_err(exc):
668 return AccessDenied(pid=pid, name=name)
669 if exc.errno == errno.ESRCH:
670 return NoSuchProcess(pid=pid, name=name)
671 raise exc
672
673
674 def wrap_exceptions(fun):
675 """Decorator which converts OSError into NoSuchProcess or AccessDenied."""
676 @functools.wraps(fun)
677 def wrapper(self, *args, **kwargs):
678 try:
679 return fun(self, *args, **kwargs)
680 except OSError as err:
681 raise convert_oserror(err, pid=self.pid, name=self._name)
682 return wrapper
683
684
685 def retry_error_partial_copy(fun):
686 """Workaround for https://github.com/giampaolo/psutil/issues/875.
687 See: https://stackoverflow.com/questions/4457745#4457745
688 """
689 @functools.wraps(fun)
690 def wrapper(self, *args, **kwargs):
691 delay = 0.0001
692 times = 33
693 for x in range(times): # retries for roughly 1 second
694 try:
695 return fun(self, *args, **kwargs)
696 except WindowsError as _:
697 err = _
698 if err.winerror == ERROR_PARTIAL_COPY:
699 time.sleep(delay)
700 delay = min(delay * 2, 0.04)
701 continue
702 else:
703 raise
704 else:
705 msg = "%s retried %s times, converted to AccessDenied as it's " \
706 "still returning %r" % (fun, times, err)
707 raise AccessDenied(pid=self.pid, name=self._name, msg=msg)
708 return wrapper
709
710
711 class Process(object):
712 """Wrapper class around underlying C implementation."""
713
714 __slots__ = ["pid", "_name", "_ppid", "_cache"]
715
716 def __init__(self, pid):
717 self.pid = pid
718 self._name = None
719 self._ppid = None
720
721 # --- oneshot() stuff
722
723 def oneshot_enter(self):
724 self._proc_info.cache_activate(self)
725 self.exe.cache_activate(self)
726
727 def oneshot_exit(self):
728 self._proc_info.cache_deactivate(self)
729 self.exe.cache_deactivate(self)
730
731 @memoize_when_activated
732 def _proc_info(self):
733 """Return multiple information about this process as a
734 raw tuple.
735 """
736 ret = cext.proc_info(self.pid)
737 assert len(ret) == len(pinfo_map)
738 return ret
739
740 def name(self):
741 """Return process name, which on Windows is always the final
742 part of the executable.
743 """
744 # This is how PIDs 0 and 4 are always represented in taskmgr
745 # and process-hacker.
746 if self.pid == 0:
747 return "System Idle Process"
748 if self.pid == 4:
749 return "System"
750 return os.path.basename(self.exe())
751
752 @wrap_exceptions
753 @memoize_when_activated
754 def exe(self):
755 if PYPY:
756 try:
757 exe = cext.proc_exe(self.pid)
758 except WindowsError as err:
759 # 24 = ERROR_TOO_MANY_OPEN_FILES. Not sure why this happens
760 # (perhaps PyPy's JIT delaying garbage collection of files?).
761 if err.errno == 24:
762 debug("%r forced into AccessDenied" % err)
763 raise AccessDenied(self.pid, self._name)
764 raise
765 else:
766 exe = cext.proc_exe(self.pid)
767 if not PY3:
768 exe = py2_strencode(exe)
769 if exe.startswith('\\'):
770 return convert_dos_path(exe)
771 return exe # May be "Registry", "MemCompression", ...
772
773 @wrap_exceptions
774 @retry_error_partial_copy
775 def cmdline(self):
776 if cext.WINVER >= cext.WINDOWS_8_1:
777 # PEB method detects cmdline changes but requires more
778 # privileges: https://github.com/giampaolo/psutil/pull/1398
779 try:
780 ret = cext.proc_cmdline(self.pid, use_peb=True)
781 except OSError as err:
782 if is_permission_err(err):
783 ret = cext.proc_cmdline(self.pid, use_peb=False)
784 else:
785 raise
786 else:
787 ret = cext.proc_cmdline(self.pid, use_peb=True)
788 if PY3:
789 return ret
790 else:
791 return [py2_strencode(s) for s in ret]
792
793 @wrap_exceptions
794 @retry_error_partial_copy
795 def environ(self):
796 ustr = cext.proc_environ(self.pid)
797 if ustr and not PY3:
798 assert isinstance(ustr, unicode), type(ustr)
799 return parse_environ_block(py2_strencode(ustr))
800
801 def ppid(self):
802 try:
803 return ppid_map()[self.pid]
804 except KeyError:
805 raise NoSuchProcess(self.pid, self._name)
806
807 def _get_raw_meminfo(self):
808 try:
809 return cext.proc_memory_info(self.pid)
810 except OSError as err:
811 if is_permission_err(err):
812 # TODO: the C ext can probably be refactored in order
813 # to get this from cext.proc_info()
814 info = self._proc_info()
815 return (
816 info[pinfo_map['num_page_faults']],
817 info[pinfo_map['peak_wset']],
818 info[pinfo_map['wset']],
819 info[pinfo_map['peak_paged_pool']],
820 info[pinfo_map['paged_pool']],
821 info[pinfo_map['peak_non_paged_pool']],
822 info[pinfo_map['non_paged_pool']],
823 info[pinfo_map['pagefile']],
824 info[pinfo_map['peak_pagefile']],
825 info[pinfo_map['mem_private']],
826 )
827 raise
828
829 @wrap_exceptions
830 def memory_info(self):
831 # on Windows RSS == WorkingSetSize and VSM == PagefileUsage.
832 # Underlying C function returns fields of PROCESS_MEMORY_COUNTERS
833 # struct.
834 t = self._get_raw_meminfo()
835 rss = t[2] # wset
836 vms = t[7] # pagefile
837 return pmem(*(rss, vms, ) + t)
838
839 @wrap_exceptions
840 def memory_full_info(self):
841 basic_mem = self.memory_info()
842 uss = cext.proc_memory_uss(self.pid)
843 uss *= getpagesize()
844 return pfullmem(*basic_mem + (uss, ))
845
846 def memory_maps(self):
847 try:
848 raw = cext.proc_memory_maps(self.pid)
849 except OSError as err:
850 # XXX - can't use wrap_exceptions decorator as we're
851 # returning a generator; probably needs refactoring.
852 raise convert_oserror(err, self.pid, self._name)
853 else:
854 for addr, perm, path, rss in raw:
855 path = convert_dos_path(path)
856 if not PY3:
857 path = py2_strencode(path)
858 addr = hex(addr)
859 yield (addr, perm, path, rss)
860
861 @wrap_exceptions
862 def kill(self):
863 return cext.proc_kill(self.pid)
864
865 @wrap_exceptions
866 def send_signal(self, sig):
867 if sig == signal.SIGTERM:
868 cext.proc_kill(self.pid)
869 # py >= 2.7
870 elif sig in (getattr(signal, "CTRL_C_EVENT", object()),
871 getattr(signal, "CTRL_BREAK_EVENT", object())):
872 os.kill(self.pid, sig)
873 else:
874 raise ValueError(
875 "only SIGTERM, CTRL_C_EVENT and CTRL_BREAK_EVENT signals "
876 "are supported on Windows")
877
878 @wrap_exceptions
879 def wait(self, timeout=None):
880 if timeout is None:
881 cext_timeout = cext.INFINITE
882 else:
883 # WaitForSingleObject() expects time in milliseconds.
884 cext_timeout = int(timeout * 1000)
885
886 timer = getattr(time, 'monotonic', time.time)
887 stop_at = timer() + timeout if timeout is not None else None
888
889 try:
890 # Exit code is supposed to come from GetExitCodeProcess().
891 # May also be None if OpenProcess() failed with
892 # ERROR_INVALID_PARAMETER, meaning PID is already gone.
893 exit_code = cext.proc_wait(self.pid, cext_timeout)
894 except cext.TimeoutExpired:
895 # WaitForSingleObject() returned WAIT_TIMEOUT. Just raise.
896 raise TimeoutExpired(timeout, self.pid, self._name)
897 except cext.TimeoutAbandoned:
898 # WaitForSingleObject() returned WAIT_ABANDONED, see:
899 # https://github.com/giampaolo/psutil/issues/1224
900 # We'll just rely on the internal polling and return None
901 # when the PID disappears. Subprocess module does the same
902 # (return None):
903 # https://github.com/python/cpython/blob/
904 # be50a7b627d0aa37e08fa8e2d5568891f19903ce/
905 # Lib/subprocess.py#L1193-L1194
906 exit_code = None
907
908 # At this point WaitForSingleObject() returned WAIT_OBJECT_0,
909 # meaning the process is gone. Stupidly there are cases where
910 # its PID may still stick around so we do a further internal
911 # polling.
912 delay = 0.0001
913 while True:
914 if not pid_exists(self.pid):
915 return exit_code
916 if stop_at and timer() >= stop_at:
917 raise TimeoutExpired(timeout, pid=self.pid, name=self._name)
918 time.sleep(delay)
919 delay = min(delay * 2, 0.04) # incremental delay
920
921 @wrap_exceptions
922 def username(self):
923 if self.pid in (0, 4):
924 return 'NT AUTHORITY\\SYSTEM'
925 domain, user = cext.proc_username(self.pid)
926 return py2_strencode(domain) + '\\' + py2_strencode(user)
927
928 @wrap_exceptions
929 def create_time(self):
930 # Note: proc_times() not put under oneshot() 'cause create_time()
931 # is already cached by the main Process class.
932 try:
933 user, system, created = cext.proc_times(self.pid)
934 return created
935 except OSError as err:
936 if is_permission_err(err):
937 return self._proc_info()[pinfo_map['create_time']]
938 raise
939
940 @wrap_exceptions
941 def num_threads(self):
942 return self._proc_info()[pinfo_map['num_threads']]
943
944 @wrap_exceptions
945 def threads(self):
946 rawlist = cext.proc_threads(self.pid)
947 retlist = []
948 for thread_id, utime, stime in rawlist:
949 ntuple = _common.pthread(thread_id, utime, stime)
950 retlist.append(ntuple)
951 return retlist
952
953 @wrap_exceptions
954 def cpu_times(self):
955 try:
956 user, system, created = cext.proc_times(self.pid)
957 except OSError as err:
958 if not is_permission_err(err):
959 raise
960 info = self._proc_info()
961 user = info[pinfo_map['user_time']]
962 system = info[pinfo_map['kernel_time']]
963 # Children user/system times are not retrievable (set to 0).
964 return _common.pcputimes(user, system, 0.0, 0.0)
965
966 @wrap_exceptions
967 def suspend(self):
968 cext.proc_suspend_or_resume(self.pid, True)
969
970 @wrap_exceptions
971 def resume(self):
972 cext.proc_suspend_or_resume(self.pid, False)
973
974 @wrap_exceptions
975 @retry_error_partial_copy
976 def cwd(self):
977 if self.pid in (0, 4):
978 raise AccessDenied(self.pid, self._name)
979 # return a normalized pathname since the native C function appends
980 # "\\" at the and of the path
981 path = cext.proc_cwd(self.pid)
982 return py2_strencode(os.path.normpath(path))
983
984 @wrap_exceptions
985 def open_files(self):
986 if self.pid in (0, 4):
987 return []
988 ret = set()
989 # Filenames come in in native format like:
990 # "\Device\HarddiskVolume1\Windows\systemew\file.txt"
991 # Convert the first part in the corresponding drive letter
992 # (e.g. "C:\") by using Windows's QueryDosDevice()
993 raw_file_names = cext.proc_open_files(self.pid)
994 for _file in raw_file_names:
995 _file = convert_dos_path(_file)
996 if isfile_strict(_file):
997 if not PY3:
998 _file = py2_strencode(_file)
999 ntuple = _common.popenfile(_file, -1)
1000 ret.add(ntuple)
1001 return list(ret)
1002
1003 @wrap_exceptions
1004 def connections(self, kind='inet'):
1005 return net_connections(kind, _pid=self.pid)
1006
1007 @wrap_exceptions
1008 def nice_get(self):
1009 value = cext.proc_priority_get(self.pid)
1010 if enum is not None:
1011 value = Priority(value)
1012 return value
1013
1014 @wrap_exceptions
1015 def nice_set(self, value):
1016 return cext.proc_priority_set(self.pid, value)
1017
1018 @wrap_exceptions
1019 def ionice_get(self):
1020 ret = cext.proc_io_priority_get(self.pid)
1021 if enum is not None:
1022 ret = IOPriority(ret)
1023 return ret
1024
1025 @wrap_exceptions
1026 def ionice_set(self, ioclass, value):
1027 if value:
1028 raise TypeError("value argument not accepted on Windows")
1029 if ioclass not in (IOPRIO_VERYLOW, IOPRIO_LOW, IOPRIO_NORMAL,
1030 IOPRIO_HIGH):
1031 raise ValueError("%s is not a valid priority" % ioclass)
1032 cext.proc_io_priority_set(self.pid, ioclass)
1033
1034 @wrap_exceptions
1035 def io_counters(self):
1036 try:
1037 ret = cext.proc_io_counters(self.pid)
1038 except OSError as err:
1039 if not is_permission_err(err):
1040 raise
1041 info = self._proc_info()
1042 ret = (
1043 info[pinfo_map['io_rcount']],
1044 info[pinfo_map['io_wcount']],
1045 info[pinfo_map['io_rbytes']],
1046 info[pinfo_map['io_wbytes']],
1047 info[pinfo_map['io_count_others']],
1048 info[pinfo_map['io_bytes_others']],
1049 )
1050 return pio(*ret)
1051
1052 @wrap_exceptions
1053 def status(self):
1054 suspended = cext.proc_is_suspended(self.pid)
1055 if suspended:
1056 return _common.STATUS_STOPPED
1057 else:
1058 return _common.STATUS_RUNNING
1059
1060 @wrap_exceptions
1061 def cpu_affinity_get(self):
1062 def from_bitmask(x):
1063 return [i for i in range(64) if (1 << i) & x]
1064 bitmask = cext.proc_cpu_affinity_get(self.pid)
1065 return from_bitmask(bitmask)
1066
1067 @wrap_exceptions
1068 def cpu_affinity_set(self, value):
1069 def to_bitmask(ls):
1070 if not ls:
1071 raise ValueError("invalid argument %r" % ls)
1072 out = 0
1073 for b in ls:
1074 out |= 2 ** b
1075 return out
1076
1077 # SetProcessAffinityMask() states that ERROR_INVALID_PARAMETER
1078 # is returned for an invalid CPU but this seems not to be true,
1079 # therefore we check CPUs validy beforehand.
1080 allcpus = list(range(len(per_cpu_times())))
1081 for cpu in value:
1082 if cpu not in allcpus:
1083 if not isinstance(cpu, (int, long)):
1084 raise TypeError(
1085 "invalid CPU %r; an integer is required" % cpu)
1086 else:
1087 raise ValueError("invalid CPU %r" % cpu)
1088
1089 bitmask = to_bitmask(value)
1090 cext.proc_cpu_affinity_set(self.pid, bitmask)
1091
1092 @wrap_exceptions
1093 def num_handles(self):
1094 try:
1095 return cext.proc_num_handles(self.pid)
1096 except OSError as err:
1097 if is_permission_err(err):
1098 return self._proc_info()[pinfo_map['num_handles']]
1099 raise
1100
1101 @wrap_exceptions
1102 def num_ctx_switches(self):
1103 ctx_switches = self._proc_info()[pinfo_map['ctx_switches']]
1104 # only voluntary ctx switches are supported
1105 return _common.pctxsw(ctx_switches, 0)