Mercurial > repos > shellac > sam_consensus_v3
comparison env/lib/python3.9/site-packages/psutil/_psosx.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 """macOS platform implementation.""" | |
| 6 | |
| 7 import contextlib | |
| 8 import errno | |
| 9 import functools | |
| 10 import os | |
| 11 from collections import namedtuple | |
| 12 | |
| 13 from . import _common | |
| 14 from . import _psposix | |
| 15 from . import _psutil_osx as cext | |
| 16 from . import _psutil_posix as cext_posix | |
| 17 from ._common import AccessDenied | |
| 18 from ._common import conn_tmap | |
| 19 from ._common import conn_to_ntuple | |
| 20 from ._common import isfile_strict | |
| 21 from ._common import memoize_when_activated | |
| 22 from ._common import NoSuchProcess | |
| 23 from ._common import parse_environ_block | |
| 24 from ._common import usage_percent | |
| 25 from ._common import ZombieProcess | |
| 26 from ._compat import PermissionError | |
| 27 from ._compat import ProcessLookupError | |
| 28 | |
| 29 | |
| 30 __extra__all__ = [] | |
| 31 | |
| 32 | |
| 33 # ===================================================================== | |
| 34 # --- globals | |
| 35 # ===================================================================== | |
| 36 | |
| 37 | |
| 38 PAGESIZE = cext_posix.getpagesize() | |
| 39 AF_LINK = cext_posix.AF_LINK | |
| 40 | |
| 41 TCP_STATUSES = { | |
| 42 cext.TCPS_ESTABLISHED: _common.CONN_ESTABLISHED, | |
| 43 cext.TCPS_SYN_SENT: _common.CONN_SYN_SENT, | |
| 44 cext.TCPS_SYN_RECEIVED: _common.CONN_SYN_RECV, | |
| 45 cext.TCPS_FIN_WAIT_1: _common.CONN_FIN_WAIT1, | |
| 46 cext.TCPS_FIN_WAIT_2: _common.CONN_FIN_WAIT2, | |
| 47 cext.TCPS_TIME_WAIT: _common.CONN_TIME_WAIT, | |
| 48 cext.TCPS_CLOSED: _common.CONN_CLOSE, | |
| 49 cext.TCPS_CLOSE_WAIT: _common.CONN_CLOSE_WAIT, | |
| 50 cext.TCPS_LAST_ACK: _common.CONN_LAST_ACK, | |
| 51 cext.TCPS_LISTEN: _common.CONN_LISTEN, | |
| 52 cext.TCPS_CLOSING: _common.CONN_CLOSING, | |
| 53 cext.PSUTIL_CONN_NONE: _common.CONN_NONE, | |
| 54 } | |
| 55 | |
| 56 PROC_STATUSES = { | |
| 57 cext.SIDL: _common.STATUS_IDLE, | |
| 58 cext.SRUN: _common.STATUS_RUNNING, | |
| 59 cext.SSLEEP: _common.STATUS_SLEEPING, | |
| 60 cext.SSTOP: _common.STATUS_STOPPED, | |
| 61 cext.SZOMB: _common.STATUS_ZOMBIE, | |
| 62 } | |
| 63 | |
| 64 kinfo_proc_map = dict( | |
| 65 ppid=0, | |
| 66 ruid=1, | |
| 67 euid=2, | |
| 68 suid=3, | |
| 69 rgid=4, | |
| 70 egid=5, | |
| 71 sgid=6, | |
| 72 ttynr=7, | |
| 73 ctime=8, | |
| 74 status=9, | |
| 75 name=10, | |
| 76 ) | |
| 77 | |
| 78 pidtaskinfo_map = dict( | |
| 79 cpuutime=0, | |
| 80 cpustime=1, | |
| 81 rss=2, | |
| 82 vms=3, | |
| 83 pfaults=4, | |
| 84 pageins=5, | |
| 85 numthreads=6, | |
| 86 volctxsw=7, | |
| 87 ) | |
| 88 | |
| 89 | |
| 90 # ===================================================================== | |
| 91 # --- named tuples | |
| 92 # ===================================================================== | |
| 93 | |
| 94 | |
| 95 # psutil.cpu_times() | |
| 96 scputimes = namedtuple('scputimes', ['user', 'nice', 'system', 'idle']) | |
| 97 # psutil.virtual_memory() | |
| 98 svmem = namedtuple( | |
| 99 'svmem', ['total', 'available', 'percent', 'used', 'free', | |
| 100 'active', 'inactive', 'wired']) | |
| 101 # psutil.Process.memory_info() | |
| 102 pmem = namedtuple('pmem', ['rss', 'vms', 'pfaults', 'pageins']) | |
| 103 # psutil.Process.memory_full_info() | |
| 104 pfullmem = namedtuple('pfullmem', pmem._fields + ('uss', )) | |
| 105 | |
| 106 | |
| 107 # ===================================================================== | |
| 108 # --- memory | |
| 109 # ===================================================================== | |
| 110 | |
| 111 | |
| 112 def virtual_memory(): | |
| 113 """System virtual memory as a namedtuple.""" | |
| 114 total, active, inactive, wired, free, speculative = cext.virtual_mem() | |
| 115 # This is how Zabbix calculate avail and used mem: | |
| 116 # https://github.com/zabbix/zabbix/blob/trunk/src/libs/zbxsysinfo/ | |
| 117 # osx/memory.c | |
| 118 # Also see: https://github.com/giampaolo/psutil/issues/1277 | |
| 119 avail = inactive + free | |
| 120 used = active + wired | |
| 121 # This is NOT how Zabbix calculates free mem but it matches "free" | |
| 122 # cmdline utility. | |
| 123 free -= speculative | |
| 124 percent = usage_percent((total - avail), total, round_=1) | |
| 125 return svmem(total, avail, percent, used, free, | |
| 126 active, inactive, wired) | |
| 127 | |
| 128 | |
| 129 def swap_memory(): | |
| 130 """Swap system memory as a (total, used, free, sin, sout) tuple.""" | |
| 131 total, used, free, sin, sout = cext.swap_mem() | |
| 132 percent = usage_percent(used, total, round_=1) | |
| 133 return _common.sswap(total, used, free, percent, sin, sout) | |
| 134 | |
| 135 | |
| 136 # ===================================================================== | |
| 137 # --- CPU | |
| 138 # ===================================================================== | |
| 139 | |
| 140 | |
| 141 def cpu_times(): | |
| 142 """Return system CPU times as a namedtuple.""" | |
| 143 user, nice, system, idle = cext.cpu_times() | |
| 144 return scputimes(user, nice, system, idle) | |
| 145 | |
| 146 | |
| 147 def per_cpu_times(): | |
| 148 """Return system CPU times as a named tuple""" | |
| 149 ret = [] | |
| 150 for cpu_t in cext.per_cpu_times(): | |
| 151 user, nice, system, idle = cpu_t | |
| 152 item = scputimes(user, nice, system, idle) | |
| 153 ret.append(item) | |
| 154 return ret | |
| 155 | |
| 156 | |
| 157 def cpu_count_logical(): | |
| 158 """Return the number of logical CPUs in the system.""" | |
| 159 return cext.cpu_count_logical() | |
| 160 | |
| 161 | |
| 162 def cpu_count_physical(): | |
| 163 """Return the number of physical CPUs in the system.""" | |
| 164 return cext.cpu_count_phys() | |
| 165 | |
| 166 | |
| 167 def cpu_stats(): | |
| 168 ctx_switches, interrupts, soft_interrupts, syscalls, traps = \ | |
| 169 cext.cpu_stats() | |
| 170 return _common.scpustats( | |
| 171 ctx_switches, interrupts, soft_interrupts, syscalls) | |
| 172 | |
| 173 | |
| 174 def cpu_freq(): | |
| 175 """Return CPU frequency. | |
| 176 On macOS per-cpu frequency is not supported. | |
| 177 Also, the returned frequency never changes, see: | |
| 178 https://arstechnica.com/civis/viewtopic.php?f=19&t=465002 | |
| 179 """ | |
| 180 curr, min_, max_ = cext.cpu_freq() | |
| 181 return [_common.scpufreq(curr, min_, max_)] | |
| 182 | |
| 183 | |
| 184 # ===================================================================== | |
| 185 # --- disks | |
| 186 # ===================================================================== | |
| 187 | |
| 188 | |
| 189 disk_usage = _psposix.disk_usage | |
| 190 disk_io_counters = cext.disk_io_counters | |
| 191 | |
| 192 | |
| 193 def disk_partitions(all=False): | |
| 194 """Return mounted disk partitions as a list of namedtuples.""" | |
| 195 retlist = [] | |
| 196 partitions = cext.disk_partitions() | |
| 197 for partition in partitions: | |
| 198 device, mountpoint, fstype, opts = partition | |
| 199 if device == 'none': | |
| 200 device = '' | |
| 201 if not all: | |
| 202 if not os.path.isabs(device) or not os.path.exists(device): | |
| 203 continue | |
| 204 maxfile = maxpath = None # set later | |
| 205 ntuple = _common.sdiskpart(device, mountpoint, fstype, opts, | |
| 206 maxfile, maxpath) | |
| 207 retlist.append(ntuple) | |
| 208 return retlist | |
| 209 | |
| 210 | |
| 211 # ===================================================================== | |
| 212 # --- sensors | |
| 213 # ===================================================================== | |
| 214 | |
| 215 | |
| 216 def sensors_battery(): | |
| 217 """Return battery information.""" | |
| 218 try: | |
| 219 percent, minsleft, power_plugged = cext.sensors_battery() | |
| 220 except NotImplementedError: | |
| 221 # no power source - return None according to interface | |
| 222 return None | |
| 223 power_plugged = power_plugged == 1 | |
| 224 if power_plugged: | |
| 225 secsleft = _common.POWER_TIME_UNLIMITED | |
| 226 elif minsleft == -1: | |
| 227 secsleft = _common.POWER_TIME_UNKNOWN | |
| 228 else: | |
| 229 secsleft = minsleft * 60 | |
| 230 return _common.sbattery(percent, secsleft, power_plugged) | |
| 231 | |
| 232 | |
| 233 # ===================================================================== | |
| 234 # --- network | |
| 235 # ===================================================================== | |
| 236 | |
| 237 | |
| 238 net_io_counters = cext.net_io_counters | |
| 239 net_if_addrs = cext_posix.net_if_addrs | |
| 240 | |
| 241 | |
| 242 def net_connections(kind='inet'): | |
| 243 """System-wide network connections.""" | |
| 244 # Note: on macOS this will fail with AccessDenied unless | |
| 245 # the process is owned by root. | |
| 246 ret = [] | |
| 247 for pid in pids(): | |
| 248 try: | |
| 249 cons = Process(pid).connections(kind) | |
| 250 except NoSuchProcess: | |
| 251 continue | |
| 252 else: | |
| 253 if cons: | |
| 254 for c in cons: | |
| 255 c = list(c) + [pid] | |
| 256 ret.append(_common.sconn(*c)) | |
| 257 return ret | |
| 258 | |
| 259 | |
| 260 def net_if_stats(): | |
| 261 """Get NIC stats (isup, duplex, speed, mtu).""" | |
| 262 names = net_io_counters().keys() | |
| 263 ret = {} | |
| 264 for name in names: | |
| 265 try: | |
| 266 mtu = cext_posix.net_if_mtu(name) | |
| 267 isup = cext_posix.net_if_is_running(name) | |
| 268 duplex, speed = cext_posix.net_if_duplex_speed(name) | |
| 269 except OSError as err: | |
| 270 # https://github.com/giampaolo/psutil/issues/1279 | |
| 271 if err.errno != errno.ENODEV: | |
| 272 raise | |
| 273 else: | |
| 274 if hasattr(_common, 'NicDuplex'): | |
| 275 duplex = _common.NicDuplex(duplex) | |
| 276 ret[name] = _common.snicstats(isup, duplex, speed, mtu) | |
| 277 return ret | |
| 278 | |
| 279 | |
| 280 # ===================================================================== | |
| 281 # --- other system functions | |
| 282 # ===================================================================== | |
| 283 | |
| 284 | |
| 285 def boot_time(): | |
| 286 """The system boot time expressed in seconds since the epoch.""" | |
| 287 return cext.boot_time() | |
| 288 | |
| 289 | |
| 290 def users(): | |
| 291 """Return currently connected users as a list of namedtuples.""" | |
| 292 retlist = [] | |
| 293 rawlist = cext.users() | |
| 294 for item in rawlist: | |
| 295 user, tty, hostname, tstamp, pid = item | |
| 296 if tty == '~': | |
| 297 continue # reboot or shutdown | |
| 298 if not tstamp: | |
| 299 continue | |
| 300 nt = _common.suser(user, tty or None, hostname or None, tstamp, pid) | |
| 301 retlist.append(nt) | |
| 302 return retlist | |
| 303 | |
| 304 | |
| 305 # ===================================================================== | |
| 306 # --- processes | |
| 307 # ===================================================================== | |
| 308 | |
| 309 | |
| 310 def pids(): | |
| 311 ls = cext.pids() | |
| 312 if 0 not in ls: | |
| 313 # On certain macOS versions pids() C doesn't return PID 0 but | |
| 314 # "ps" does and the process is querable via sysctl(): | |
| 315 # https://travis-ci.org/giampaolo/psutil/jobs/309619941 | |
| 316 try: | |
| 317 Process(0).create_time() | |
| 318 ls.insert(0, 0) | |
| 319 except NoSuchProcess: | |
| 320 pass | |
| 321 except AccessDenied: | |
| 322 ls.insert(0, 0) | |
| 323 return ls | |
| 324 | |
| 325 | |
| 326 pid_exists = _psposix.pid_exists | |
| 327 | |
| 328 | |
| 329 def is_zombie(pid): | |
| 330 try: | |
| 331 st = cext.proc_kinfo_oneshot(pid)[kinfo_proc_map['status']] | |
| 332 return st == cext.SZOMB | |
| 333 except Exception: | |
| 334 return False | |
| 335 | |
| 336 | |
| 337 def wrap_exceptions(fun): | |
| 338 """Decorator which translates bare OSError exceptions into | |
| 339 NoSuchProcess and AccessDenied. | |
| 340 """ | |
| 341 @functools.wraps(fun) | |
| 342 def wrapper(self, *args, **kwargs): | |
| 343 try: | |
| 344 return fun(self, *args, **kwargs) | |
| 345 except ProcessLookupError: | |
| 346 if is_zombie(self.pid): | |
| 347 raise ZombieProcess(self.pid, self._name, self._ppid) | |
| 348 else: | |
| 349 raise NoSuchProcess(self.pid, self._name) | |
| 350 except PermissionError: | |
| 351 raise AccessDenied(self.pid, self._name) | |
| 352 except cext.ZombieProcessError: | |
| 353 raise ZombieProcess(self.pid, self._name, self._ppid) | |
| 354 return wrapper | |
| 355 | |
| 356 | |
| 357 @contextlib.contextmanager | |
| 358 def catch_zombie(proc): | |
| 359 """There are some poor C APIs which incorrectly raise ESRCH when | |
| 360 the process is still alive or it's a zombie, or even RuntimeError | |
| 361 (those who don't set errno). This is here in order to solve: | |
| 362 https://github.com/giampaolo/psutil/issues/1044 | |
| 363 """ | |
| 364 try: | |
| 365 yield | |
| 366 except (OSError, RuntimeError) as err: | |
| 367 if isinstance(err, RuntimeError) or err.errno == errno.ESRCH: | |
| 368 try: | |
| 369 # status() is not supposed to lie and correctly detect | |
| 370 # zombies so if it raises ESRCH it's true. | |
| 371 status = proc.status() | |
| 372 except NoSuchProcess: | |
| 373 raise err | |
| 374 else: | |
| 375 if status == _common.STATUS_ZOMBIE: | |
| 376 raise ZombieProcess(proc.pid, proc._name, proc._ppid) | |
| 377 else: | |
| 378 raise AccessDenied(proc.pid, proc._name) | |
| 379 else: | |
| 380 raise | |
| 381 | |
| 382 | |
| 383 class Process(object): | |
| 384 """Wrapper class around underlying C implementation.""" | |
| 385 | |
| 386 __slots__ = ["pid", "_name", "_ppid", "_cache"] | |
| 387 | |
| 388 def __init__(self, pid): | |
| 389 self.pid = pid | |
| 390 self._name = None | |
| 391 self._ppid = None | |
| 392 | |
| 393 @wrap_exceptions | |
| 394 @memoize_when_activated | |
| 395 def _get_kinfo_proc(self): | |
| 396 # Note: should work with all PIDs without permission issues. | |
| 397 ret = cext.proc_kinfo_oneshot(self.pid) | |
| 398 assert len(ret) == len(kinfo_proc_map) | |
| 399 return ret | |
| 400 | |
| 401 @wrap_exceptions | |
| 402 @memoize_when_activated | |
| 403 def _get_pidtaskinfo(self): | |
| 404 # Note: should work for PIDs owned by user only. | |
| 405 with catch_zombie(self): | |
| 406 ret = cext.proc_pidtaskinfo_oneshot(self.pid) | |
| 407 assert len(ret) == len(pidtaskinfo_map) | |
| 408 return ret | |
| 409 | |
| 410 def oneshot_enter(self): | |
| 411 self._get_kinfo_proc.cache_activate(self) | |
| 412 self._get_pidtaskinfo.cache_activate(self) | |
| 413 | |
| 414 def oneshot_exit(self): | |
| 415 self._get_kinfo_proc.cache_deactivate(self) | |
| 416 self._get_pidtaskinfo.cache_deactivate(self) | |
| 417 | |
| 418 @wrap_exceptions | |
| 419 def name(self): | |
| 420 name = self._get_kinfo_proc()[kinfo_proc_map['name']] | |
| 421 return name if name is not None else cext.proc_name(self.pid) | |
| 422 | |
| 423 @wrap_exceptions | |
| 424 def exe(self): | |
| 425 with catch_zombie(self): | |
| 426 return cext.proc_exe(self.pid) | |
| 427 | |
| 428 @wrap_exceptions | |
| 429 def cmdline(self): | |
| 430 with catch_zombie(self): | |
| 431 return cext.proc_cmdline(self.pid) | |
| 432 | |
| 433 @wrap_exceptions | |
| 434 def environ(self): | |
| 435 with catch_zombie(self): | |
| 436 return parse_environ_block(cext.proc_environ(self.pid)) | |
| 437 | |
| 438 @wrap_exceptions | |
| 439 def ppid(self): | |
| 440 self._ppid = self._get_kinfo_proc()[kinfo_proc_map['ppid']] | |
| 441 return self._ppid | |
| 442 | |
| 443 @wrap_exceptions | |
| 444 def cwd(self): | |
| 445 with catch_zombie(self): | |
| 446 return cext.proc_cwd(self.pid) | |
| 447 | |
| 448 @wrap_exceptions | |
| 449 def uids(self): | |
| 450 rawtuple = self._get_kinfo_proc() | |
| 451 return _common.puids( | |
| 452 rawtuple[kinfo_proc_map['ruid']], | |
| 453 rawtuple[kinfo_proc_map['euid']], | |
| 454 rawtuple[kinfo_proc_map['suid']]) | |
| 455 | |
| 456 @wrap_exceptions | |
| 457 def gids(self): | |
| 458 rawtuple = self._get_kinfo_proc() | |
| 459 return _common.puids( | |
| 460 rawtuple[kinfo_proc_map['rgid']], | |
| 461 rawtuple[kinfo_proc_map['egid']], | |
| 462 rawtuple[kinfo_proc_map['sgid']]) | |
| 463 | |
| 464 @wrap_exceptions | |
| 465 def terminal(self): | |
| 466 tty_nr = self._get_kinfo_proc()[kinfo_proc_map['ttynr']] | |
| 467 tmap = _psposix.get_terminal_map() | |
| 468 try: | |
| 469 return tmap[tty_nr] | |
| 470 except KeyError: | |
| 471 return None | |
| 472 | |
| 473 @wrap_exceptions | |
| 474 def memory_info(self): | |
| 475 rawtuple = self._get_pidtaskinfo() | |
| 476 return pmem( | |
| 477 rawtuple[pidtaskinfo_map['rss']], | |
| 478 rawtuple[pidtaskinfo_map['vms']], | |
| 479 rawtuple[pidtaskinfo_map['pfaults']], | |
| 480 rawtuple[pidtaskinfo_map['pageins']], | |
| 481 ) | |
| 482 | |
| 483 @wrap_exceptions | |
| 484 def memory_full_info(self): | |
| 485 basic_mem = self.memory_info() | |
| 486 uss = cext.proc_memory_uss(self.pid) | |
| 487 return pfullmem(*basic_mem + (uss, )) | |
| 488 | |
| 489 @wrap_exceptions | |
| 490 def cpu_times(self): | |
| 491 rawtuple = self._get_pidtaskinfo() | |
| 492 return _common.pcputimes( | |
| 493 rawtuple[pidtaskinfo_map['cpuutime']], | |
| 494 rawtuple[pidtaskinfo_map['cpustime']], | |
| 495 # children user / system times are not retrievable (set to 0) | |
| 496 0.0, 0.0) | |
| 497 | |
| 498 @wrap_exceptions | |
| 499 def create_time(self): | |
| 500 return self._get_kinfo_proc()[kinfo_proc_map['ctime']] | |
| 501 | |
| 502 @wrap_exceptions | |
| 503 def num_ctx_switches(self): | |
| 504 # Unvoluntary value seems not to be available; | |
| 505 # getrusage() numbers seems to confirm this theory. | |
| 506 # We set it to 0. | |
| 507 vol = self._get_pidtaskinfo()[pidtaskinfo_map['volctxsw']] | |
| 508 return _common.pctxsw(vol, 0) | |
| 509 | |
| 510 @wrap_exceptions | |
| 511 def num_threads(self): | |
| 512 return self._get_pidtaskinfo()[pidtaskinfo_map['numthreads']] | |
| 513 | |
| 514 @wrap_exceptions | |
| 515 def open_files(self): | |
| 516 if self.pid == 0: | |
| 517 return [] | |
| 518 files = [] | |
| 519 with catch_zombie(self): | |
| 520 rawlist = cext.proc_open_files(self.pid) | |
| 521 for path, fd in rawlist: | |
| 522 if isfile_strict(path): | |
| 523 ntuple = _common.popenfile(path, fd) | |
| 524 files.append(ntuple) | |
| 525 return files | |
| 526 | |
| 527 @wrap_exceptions | |
| 528 def connections(self, kind='inet'): | |
| 529 if kind not in conn_tmap: | |
| 530 raise ValueError("invalid %r kind argument; choose between %s" | |
| 531 % (kind, ', '.join([repr(x) for x in conn_tmap]))) | |
| 532 families, types = conn_tmap[kind] | |
| 533 with catch_zombie(self): | |
| 534 rawlist = cext.proc_connections(self.pid, families, types) | |
| 535 ret = [] | |
| 536 for item in rawlist: | |
| 537 fd, fam, type, laddr, raddr, status = item | |
| 538 nt = conn_to_ntuple(fd, fam, type, laddr, raddr, status, | |
| 539 TCP_STATUSES) | |
| 540 ret.append(nt) | |
| 541 return ret | |
| 542 | |
| 543 @wrap_exceptions | |
| 544 def num_fds(self): | |
| 545 if self.pid == 0: | |
| 546 return 0 | |
| 547 with catch_zombie(self): | |
| 548 return cext.proc_num_fds(self.pid) | |
| 549 | |
| 550 @wrap_exceptions | |
| 551 def wait(self, timeout=None): | |
| 552 return _psposix.wait_pid(self.pid, timeout, self._name) | |
| 553 | |
| 554 @wrap_exceptions | |
| 555 def nice_get(self): | |
| 556 with catch_zombie(self): | |
| 557 return cext_posix.getpriority(self.pid) | |
| 558 | |
| 559 @wrap_exceptions | |
| 560 def nice_set(self, value): | |
| 561 with catch_zombie(self): | |
| 562 return cext_posix.setpriority(self.pid, value) | |
| 563 | |
| 564 @wrap_exceptions | |
| 565 def status(self): | |
| 566 code = self._get_kinfo_proc()[kinfo_proc_map['status']] | |
| 567 # XXX is '?' legit? (we're not supposed to return it anyway) | |
| 568 return PROC_STATUSES.get(code, '?') | |
| 569 | |
| 570 @wrap_exceptions | |
| 571 def threads(self): | |
| 572 rawlist = cext.proc_threads(self.pid) | |
| 573 retlist = [] | |
| 574 for thread_id, utime, stime in rawlist: | |
| 575 ntuple = _common.pthread(thread_id, utime, stime) | |
| 576 retlist.append(ntuple) | |
| 577 return retlist |
