Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/psutil/_pssunos.py: 4%

426 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-06-07 06:35 +0000

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"""Sun OS Solaris platform implementation.""" 

6 

7import errno 

8import functools 

9import os 

10import socket 

11import subprocess 

12import sys 

13from collections import namedtuple 

14from socket import AF_INET 

15 

16from . import _common 

17from . import _psposix 

18from . import _psutil_posix as cext_posix 

19from . import _psutil_sunos as cext 

20from ._common import AF_INET6 

21from ._common import AccessDenied 

22from ._common import NoSuchProcess 

23from ._common import ZombieProcess 

24from ._common import debug 

25from ._common import get_procfs_path 

26from ._common import isfile_strict 

27from ._common import memoize_when_activated 

28from ._common import sockfam_to_enum 

29from ._common import socktype_to_enum 

30from ._common import usage_percent 

31from ._compat import PY3 

32from ._compat import FileNotFoundError 

33from ._compat import PermissionError 

34from ._compat import ProcessLookupError 

35from ._compat import b 

36 

37 

38__extra__all__ = ["CONN_IDLE", "CONN_BOUND", "PROCFS_PATH"] 

39 

40 

41# ===================================================================== 

42# --- globals 

43# ===================================================================== 

44 

45 

46PAGE_SIZE = cext_posix.getpagesize() 

47AF_LINK = cext_posix.AF_LINK 

48IS_64_BIT = sys.maxsize > 2**32 

49 

50CONN_IDLE = "IDLE" 

51CONN_BOUND = "BOUND" 

52 

53PROC_STATUSES = { 

54 cext.SSLEEP: _common.STATUS_SLEEPING, 

55 cext.SRUN: _common.STATUS_RUNNING, 

56 cext.SZOMB: _common.STATUS_ZOMBIE, 

57 cext.SSTOP: _common.STATUS_STOPPED, 

58 cext.SIDL: _common.STATUS_IDLE, 

59 cext.SONPROC: _common.STATUS_RUNNING, # same as run 

60 cext.SWAIT: _common.STATUS_WAITING, 

61} 

62 

63TCP_STATUSES = { 

64 cext.TCPS_ESTABLISHED: _common.CONN_ESTABLISHED, 

65 cext.TCPS_SYN_SENT: _common.CONN_SYN_SENT, 

66 cext.TCPS_SYN_RCVD: _common.CONN_SYN_RECV, 

67 cext.TCPS_FIN_WAIT_1: _common.CONN_FIN_WAIT1, 

68 cext.TCPS_FIN_WAIT_2: _common.CONN_FIN_WAIT2, 

69 cext.TCPS_TIME_WAIT: _common.CONN_TIME_WAIT, 

70 cext.TCPS_CLOSED: _common.CONN_CLOSE, 

71 cext.TCPS_CLOSE_WAIT: _common.CONN_CLOSE_WAIT, 

72 cext.TCPS_LAST_ACK: _common.CONN_LAST_ACK, 

73 cext.TCPS_LISTEN: _common.CONN_LISTEN, 

74 cext.TCPS_CLOSING: _common.CONN_CLOSING, 

75 cext.PSUTIL_CONN_NONE: _common.CONN_NONE, 

76 cext.TCPS_IDLE: CONN_IDLE, # sunos specific 

77 cext.TCPS_BOUND: CONN_BOUND, # sunos specific 

78} 

79 

80proc_info_map = dict( 

81 ppid=0, 

82 rss=1, 

83 vms=2, 

84 create_time=3, 

85 nice=4, 

86 num_threads=5, 

87 status=6, 

88 ttynr=7, 

89 uid=8, 

90 euid=9, 

91 gid=10, 

92 egid=11) 

93 

94 

95# ===================================================================== 

96# --- named tuples 

97# ===================================================================== 

98 

99 

100# psutil.cpu_times() 

101scputimes = namedtuple('scputimes', ['user', 'system', 'idle', 'iowait']) 

102# psutil.cpu_times(percpu=True) 

103pcputimes = namedtuple('pcputimes', 

104 ['user', 'system', 'children_user', 'children_system']) 

105# psutil.virtual_memory() 

106svmem = namedtuple('svmem', ['total', 'available', 'percent', 'used', 'free']) 

107# psutil.Process.memory_info() 

108pmem = namedtuple('pmem', ['rss', 'vms']) 

109pfullmem = pmem 

110# psutil.Process.memory_maps(grouped=True) 

111pmmap_grouped = namedtuple('pmmap_grouped', 

112 ['path', 'rss', 'anonymous', 'locked']) 

113# psutil.Process.memory_maps(grouped=False) 

114pmmap_ext = namedtuple( 

115 'pmmap_ext', 'addr perms ' + ' '.join(pmmap_grouped._fields)) 

116 

117 

118# ===================================================================== 

119# --- memory 

120# ===================================================================== 

121 

122 

123def virtual_memory(): 

124 """Report virtual memory metrics.""" 

125 # we could have done this with kstat, but IMHO this is good enough 

126 total = os.sysconf('SC_PHYS_PAGES') * PAGE_SIZE 

127 # note: there's no difference on Solaris 

128 free = avail = os.sysconf('SC_AVPHYS_PAGES') * PAGE_SIZE 

129 used = total - free 

130 percent = usage_percent(used, total, round_=1) 

131 return svmem(total, avail, percent, used, free) 

132 

133 

134def swap_memory(): 

135 """Report swap memory metrics.""" 

136 sin, sout = cext.swap_mem() 

137 # XXX 

138 # we are supposed to get total/free by doing so: 

139 # http://cvs.opensolaris.org/source/xref/onnv/onnv-gate/ 

140 # usr/src/cmd/swap/swap.c 

141 # ...nevertheless I can't manage to obtain the same numbers as 'swap' 

142 # cmdline utility, so let's parse its output (sigh!) 

143 p = subprocess.Popen(['/usr/bin/env', 'PATH=/usr/sbin:/sbin:%s' % 

144 os.environ['PATH'], 'swap', '-l'], 

145 stdout=subprocess.PIPE) 

146 stdout, _ = p.communicate() 

147 if PY3: 

148 stdout = stdout.decode(sys.stdout.encoding) 

149 if p.returncode != 0: 

150 raise RuntimeError("'swap -l' failed (retcode=%s)" % p.returncode) 

151 

152 lines = stdout.strip().split('\n')[1:] 

153 if not lines: 

154 raise RuntimeError('no swap device(s) configured') 

155 total = free = 0 

156 for line in lines: 

157 line = line.split() 

158 t, f = line[3:5] 

159 total += int(int(t) * 512) 

160 free += int(int(f) * 512) 

161 used = total - free 

162 percent = usage_percent(used, total, round_=1) 

163 return _common.sswap(total, used, free, percent, 

164 sin * PAGE_SIZE, sout * PAGE_SIZE) 

165 

166 

167# ===================================================================== 

168# --- CPU 

169# ===================================================================== 

170 

171 

172def cpu_times(): 

173 """Return system-wide CPU times as a named tuple""" 

174 ret = cext.per_cpu_times() 

175 return scputimes(*[sum(x) for x in zip(*ret)]) 

176 

177 

178def per_cpu_times(): 

179 """Return system per-CPU times as a list of named tuples""" 

180 ret = cext.per_cpu_times() 

181 return [scputimes(*x) for x in ret] 

182 

183 

184def cpu_count_logical(): 

185 """Return the number of logical CPUs in the system.""" 

186 try: 

187 return os.sysconf("SC_NPROCESSORS_ONLN") 

188 except ValueError: 

189 # mimic os.cpu_count() behavior 

190 return None 

191 

192 

193def cpu_count_cores(): 

194 """Return the number of CPU cores in the system.""" 

195 return cext.cpu_count_cores() 

196 

197 

198def cpu_stats(): 

199 """Return various CPU stats as a named tuple.""" 

200 ctx_switches, interrupts, syscalls, traps = cext.cpu_stats() 

201 soft_interrupts = 0 

202 return _common.scpustats(ctx_switches, interrupts, soft_interrupts, 

203 syscalls) 

204 

205 

206# ===================================================================== 

207# --- disks 

208# ===================================================================== 

209 

210 

211disk_io_counters = cext.disk_io_counters 

212disk_usage = _psposix.disk_usage 

213 

214 

215def disk_partitions(all=False): 

216 """Return system disk partitions.""" 

217 # TODO - the filtering logic should be better checked so that 

218 # it tries to reflect 'df' as much as possible 

219 retlist = [] 

220 partitions = cext.disk_partitions() 

221 for partition in partitions: 

222 device, mountpoint, fstype, opts = partition 

223 if device == 'none': 

224 device = '' 

225 if not all: 

226 # Differently from, say, Linux, we don't have a list of 

227 # common fs types so the best we can do, AFAIK, is to 

228 # filter by filesystem having a total size > 0. 

229 try: 

230 if not disk_usage(mountpoint).total: 

231 continue 

232 except OSError as err: 

233 # https://github.com/giampaolo/psutil/issues/1674 

234 debug("skipping %r: %s" % (mountpoint, err)) 

235 continue 

236 maxfile = maxpath = None # set later 

237 ntuple = _common.sdiskpart(device, mountpoint, fstype, opts, 

238 maxfile, maxpath) 

239 retlist.append(ntuple) 

240 return retlist 

241 

242 

243# ===================================================================== 

244# --- network 

245# ===================================================================== 

246 

247 

248net_io_counters = cext.net_io_counters 

249net_if_addrs = cext_posix.net_if_addrs 

250 

251 

252def net_connections(kind, _pid=-1): 

253 """Return socket connections. If pid == -1 return system-wide 

254 connections (as opposed to connections opened by one process only). 

255 Only INET sockets are returned (UNIX are not). 

256 """ 

257 cmap = _common.conn_tmap.copy() 

258 if _pid == -1: 

259 cmap.pop('unix', 0) 

260 if kind not in cmap: 

261 raise ValueError("invalid %r kind argument; choose between %s" 

262 % (kind, ', '.join([repr(x) for x in cmap]))) 

263 families, types = _common.conn_tmap[kind] 

264 rawlist = cext.net_connections(_pid) 

265 ret = set() 

266 for item in rawlist: 

267 fd, fam, type_, laddr, raddr, status, pid = item 

268 if fam not in families: 

269 continue 

270 if type_ not in types: 

271 continue 

272 # TODO: refactor and use _common.conn_to_ntuple. 

273 if fam in (AF_INET, AF_INET6): 

274 if laddr: 

275 laddr = _common.addr(*laddr) 

276 if raddr: 

277 raddr = _common.addr(*raddr) 

278 status = TCP_STATUSES[status] 

279 fam = sockfam_to_enum(fam) 

280 type_ = socktype_to_enum(type_) 

281 if _pid == -1: 

282 nt = _common.sconn(fd, fam, type_, laddr, raddr, status, pid) 

283 else: 

284 nt = _common.pconn(fd, fam, type_, laddr, raddr, status) 

285 ret.add(nt) 

286 return list(ret) 

287 

288 

289def net_if_stats(): 

290 """Get NIC stats (isup, duplex, speed, mtu).""" 

291 ret = cext.net_if_stats() 

292 for name, items in ret.items(): 

293 isup, duplex, speed, mtu = items 

294 if hasattr(_common, 'NicDuplex'): 

295 duplex = _common.NicDuplex(duplex) 

296 ret[name] = _common.snicstats(isup, duplex, speed, mtu, '') 

297 return ret 

298 

299 

300# ===================================================================== 

301# --- other system functions 

302# ===================================================================== 

303 

304 

305def boot_time(): 

306 """The system boot time expressed in seconds since the epoch.""" 

307 return cext.boot_time() 

308 

309 

310def users(): 

311 """Return currently connected users as a list of namedtuples.""" 

312 retlist = [] 

313 rawlist = cext.users() 

314 localhost = (':0.0', ':0') 

315 for item in rawlist: 

316 user, tty, hostname, tstamp, user_process, pid = item 

317 # note: the underlying C function includes entries about 

318 # system boot, run level and others. We might want 

319 # to use them in the future. 

320 if not user_process: 

321 continue 

322 if hostname in localhost: 

323 hostname = 'localhost' 

324 nt = _common.suser(user, tty, hostname, tstamp, pid) 

325 retlist.append(nt) 

326 return retlist 

327 

328 

329# ===================================================================== 

330# --- processes 

331# ===================================================================== 

332 

333 

334def pids(): 

335 """Returns a list of PIDs currently running on the system.""" 

336 return [int(x) for x in os.listdir(b(get_procfs_path())) if x.isdigit()] 

337 

338 

339def pid_exists(pid): 

340 """Check for the existence of a unix pid.""" 

341 return _psposix.pid_exists(pid) 

342 

343 

344def wrap_exceptions(fun): 

345 """Call callable into a try/except clause and translate ENOENT, 

346 EACCES and EPERM in NoSuchProcess or AccessDenied exceptions. 

347 """ 

348 @functools.wraps(fun) 

349 def wrapper(self, *args, **kwargs): 

350 try: 

351 return fun(self, *args, **kwargs) 

352 except (FileNotFoundError, ProcessLookupError): 

353 # ENOENT (no such file or directory) gets raised on open(). 

354 # ESRCH (no such process) can get raised on read() if 

355 # process is gone in meantime. 

356 if not pid_exists(self.pid): 

357 raise NoSuchProcess(self.pid, self._name) 

358 else: 

359 raise ZombieProcess(self.pid, self._name, self._ppid) 

360 except PermissionError: 

361 raise AccessDenied(self.pid, self._name) 

362 except OSError: 

363 if self.pid == 0: 

364 if 0 in pids(): 

365 raise AccessDenied(self.pid, self._name) 

366 else: 

367 raise 

368 raise 

369 return wrapper 

370 

371 

372class Process(object): 

373 """Wrapper class around underlying C implementation.""" 

374 

375 __slots__ = ["pid", "_name", "_ppid", "_procfs_path", "_cache"] 

376 

377 def __init__(self, pid): 

378 self.pid = pid 

379 self._name = None 

380 self._ppid = None 

381 self._procfs_path = get_procfs_path() 

382 

383 def _assert_alive(self): 

384 """Raise NSP if the process disappeared on us.""" 

385 # For those C function who do not raise NSP, possibly returning 

386 # incorrect or incomplete result. 

387 os.stat('%s/%s' % (self._procfs_path, self.pid)) 

388 

389 def oneshot_enter(self): 

390 self._proc_name_and_args.cache_activate(self) 

391 self._proc_basic_info.cache_activate(self) 

392 self._proc_cred.cache_activate(self) 

393 

394 def oneshot_exit(self): 

395 self._proc_name_and_args.cache_deactivate(self) 

396 self._proc_basic_info.cache_deactivate(self) 

397 self._proc_cred.cache_deactivate(self) 

398 

399 @wrap_exceptions 

400 @memoize_when_activated 

401 def _proc_name_and_args(self): 

402 return cext.proc_name_and_args(self.pid, self._procfs_path) 

403 

404 @wrap_exceptions 

405 @memoize_when_activated 

406 def _proc_basic_info(self): 

407 if self.pid == 0 and not \ 

408 os.path.exists('%s/%s/psinfo' % (self._procfs_path, self.pid)): 

409 raise AccessDenied(self.pid) 

410 ret = cext.proc_basic_info(self.pid, self._procfs_path) 

411 assert len(ret) == len(proc_info_map) 

412 return ret 

413 

414 @wrap_exceptions 

415 @memoize_when_activated 

416 def _proc_cred(self): 

417 return cext.proc_cred(self.pid, self._procfs_path) 

418 

419 @wrap_exceptions 

420 def name(self): 

421 # note: max len == 15 

422 return self._proc_name_and_args()[0] 

423 

424 @wrap_exceptions 

425 def exe(self): 

426 try: 

427 return os.readlink( 

428 "%s/%s/path/a.out" % (self._procfs_path, self.pid)) 

429 except OSError: 

430 pass # continue and guess the exe name from the cmdline 

431 # Will be guessed later from cmdline but we want to explicitly 

432 # invoke cmdline here in order to get an AccessDenied 

433 # exception if the user has not enough privileges. 

434 self.cmdline() 

435 return "" 

436 

437 @wrap_exceptions 

438 def cmdline(self): 

439 return self._proc_name_and_args()[1].split(' ') 

440 

441 @wrap_exceptions 

442 def environ(self): 

443 return cext.proc_environ(self.pid, self._procfs_path) 

444 

445 @wrap_exceptions 

446 def create_time(self): 

447 return self._proc_basic_info()[proc_info_map['create_time']] 

448 

449 @wrap_exceptions 

450 def num_threads(self): 

451 return self._proc_basic_info()[proc_info_map['num_threads']] 

452 

453 @wrap_exceptions 

454 def nice_get(self): 

455 # Note #1: getpriority(3) doesn't work for realtime processes. 

456 # Psinfo is what ps uses, see: 

457 # https://github.com/giampaolo/psutil/issues/1194 

458 return self._proc_basic_info()[proc_info_map['nice']] 

459 

460 @wrap_exceptions 

461 def nice_set(self, value): 

462 if self.pid in (2, 3): 

463 # Special case PIDs: internally setpriority(3) return ESRCH 

464 # (no such process), no matter what. 

465 # The process actually exists though, as it has a name, 

466 # creation time, etc. 

467 raise AccessDenied(self.pid, self._name) 

468 return cext_posix.setpriority(self.pid, value) 

469 

470 @wrap_exceptions 

471 def ppid(self): 

472 self._ppid = self._proc_basic_info()[proc_info_map['ppid']] 

473 return self._ppid 

474 

475 @wrap_exceptions 

476 def uids(self): 

477 try: 

478 real, effective, saved, _, _, _ = self._proc_cred() 

479 except AccessDenied: 

480 real = self._proc_basic_info()[proc_info_map['uid']] 

481 effective = self._proc_basic_info()[proc_info_map['euid']] 

482 saved = None 

483 return _common.puids(real, effective, saved) 

484 

485 @wrap_exceptions 

486 def gids(self): 

487 try: 

488 _, _, _, real, effective, saved = self._proc_cred() 

489 except AccessDenied: 

490 real = self._proc_basic_info()[proc_info_map['gid']] 

491 effective = self._proc_basic_info()[proc_info_map['egid']] 

492 saved = None 

493 return _common.puids(real, effective, saved) 

494 

495 @wrap_exceptions 

496 def cpu_times(self): 

497 try: 

498 times = cext.proc_cpu_times(self.pid, self._procfs_path) 

499 except OSError as err: 

500 if err.errno == errno.EOVERFLOW and not IS_64_BIT: 

501 # We may get here if we attempt to query a 64bit process 

502 # with a 32bit python. 

503 # Error originates from read() and also tools like "cat" 

504 # fail in the same way (!). 

505 # Since there simply is no way to determine CPU times we 

506 # return 0.0 as a fallback. See: 

507 # https://github.com/giampaolo/psutil/issues/857 

508 times = (0.0, 0.0, 0.0, 0.0) 

509 else: 

510 raise 

511 return _common.pcputimes(*times) 

512 

513 @wrap_exceptions 

514 def cpu_num(self): 

515 return cext.proc_cpu_num(self.pid, self._procfs_path) 

516 

517 @wrap_exceptions 

518 def terminal(self): 

519 procfs_path = self._procfs_path 

520 hit_enoent = False 

521 tty = wrap_exceptions( 

522 self._proc_basic_info()[proc_info_map['ttynr']]) 

523 if tty != cext.PRNODEV: 

524 for x in (0, 1, 2, 255): 

525 try: 

526 return os.readlink( 

527 '%s/%d/path/%d' % (procfs_path, self.pid, x)) 

528 except FileNotFoundError: 

529 hit_enoent = True 

530 continue 

531 if hit_enoent: 

532 self._assert_alive() 

533 

534 @wrap_exceptions 

535 def cwd(self): 

536 # /proc/PID/path/cwd may not be resolved by readlink() even if 

537 # it exists (ls shows it). If that's the case and the process 

538 # is still alive return None (we can return None also on BSD). 

539 # Reference: http://goo.gl/55XgO 

540 procfs_path = self._procfs_path 

541 try: 

542 return os.readlink("%s/%s/path/cwd" % (procfs_path, self.pid)) 

543 except FileNotFoundError: 

544 os.stat("%s/%s" % (procfs_path, self.pid)) # raise NSP or AD 

545 return "" 

546 

547 @wrap_exceptions 

548 def memory_info(self): 

549 ret = self._proc_basic_info() 

550 rss = ret[proc_info_map['rss']] * 1024 

551 vms = ret[proc_info_map['vms']] * 1024 

552 return pmem(rss, vms) 

553 

554 memory_full_info = memory_info 

555 

556 @wrap_exceptions 

557 def status(self): 

558 code = self._proc_basic_info()[proc_info_map['status']] 

559 # XXX is '?' legit? (we're not supposed to return it anyway) 

560 return PROC_STATUSES.get(code, '?') 

561 

562 @wrap_exceptions 

563 def threads(self): 

564 procfs_path = self._procfs_path 

565 ret = [] 

566 tids = os.listdir('%s/%d/lwp' % (procfs_path, self.pid)) 

567 hit_enoent = False 

568 for tid in tids: 

569 tid = int(tid) 

570 try: 

571 utime, stime = cext.query_process_thread( 

572 self.pid, tid, procfs_path) 

573 except EnvironmentError as err: 

574 if err.errno == errno.EOVERFLOW and not IS_64_BIT: 

575 # We may get here if we attempt to query a 64bit process 

576 # with a 32bit python. 

577 # Error originates from read() and also tools like "cat" 

578 # fail in the same way (!). 

579 # Since there simply is no way to determine CPU times we 

580 # return 0.0 as a fallback. See: 

581 # https://github.com/giampaolo/psutil/issues/857 

582 continue 

583 # ENOENT == thread gone in meantime 

584 if err.errno == errno.ENOENT: 

585 hit_enoent = True 

586 continue 

587 raise 

588 else: 

589 nt = _common.pthread(tid, utime, stime) 

590 ret.append(nt) 

591 if hit_enoent: 

592 self._assert_alive() 

593 return ret 

594 

595 @wrap_exceptions 

596 def open_files(self): 

597 retlist = [] 

598 hit_enoent = False 

599 procfs_path = self._procfs_path 

600 pathdir = '%s/%d/path' % (procfs_path, self.pid) 

601 for fd in os.listdir('%s/%d/fd' % (procfs_path, self.pid)): 

602 path = os.path.join(pathdir, fd) 

603 if os.path.islink(path): 

604 try: 

605 file = os.readlink(path) 

606 except FileNotFoundError: 

607 hit_enoent = True 

608 continue 

609 else: 

610 if isfile_strict(file): 

611 retlist.append(_common.popenfile(file, int(fd))) 

612 if hit_enoent: 

613 self._assert_alive() 

614 return retlist 

615 

616 def _get_unix_sockets(self, pid): 

617 """Get UNIX sockets used by process by parsing 'pfiles' output.""" 

618 # TODO: rewrite this in C (...but the damn netstat source code 

619 # does not include this part! Argh!!) 

620 cmd = "pfiles %s" % pid 

621 p = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, 

622 stderr=subprocess.PIPE) 

623 stdout, stderr = p.communicate() 

624 if PY3: 

625 stdout, stderr = [x.decode(sys.stdout.encoding) 

626 for x in (stdout, stderr)] 

627 if p.returncode != 0: 

628 if 'permission denied' in stderr.lower(): 

629 raise AccessDenied(self.pid, self._name) 

630 if 'no such process' in stderr.lower(): 

631 raise NoSuchProcess(self.pid, self._name) 

632 raise RuntimeError("%r command error\n%s" % (cmd, stderr)) 

633 

634 lines = stdout.split('\n')[2:] 

635 for i, line in enumerate(lines): 

636 line = line.lstrip() 

637 if line.startswith('sockname: AF_UNIX'): 

638 path = line.split(' ', 2)[2] 

639 type = lines[i - 2].strip() 

640 if type == 'SOCK_STREAM': 

641 type = socket.SOCK_STREAM 

642 elif type == 'SOCK_DGRAM': 

643 type = socket.SOCK_DGRAM 

644 else: 

645 type = -1 

646 yield (-1, socket.AF_UNIX, type, path, "", _common.CONN_NONE) 

647 

648 @wrap_exceptions 

649 def connections(self, kind='inet'): 

650 ret = net_connections(kind, _pid=self.pid) 

651 # The underlying C implementation retrieves all OS connections 

652 # and filters them by PID. At this point we can't tell whether 

653 # an empty list means there were no connections for process or 

654 # process is no longer active so we force NSP in case the PID 

655 # is no longer there. 

656 if not ret: 

657 # will raise NSP if process is gone 

658 os.stat('%s/%s' % (self._procfs_path, self.pid)) 

659 

660 # UNIX sockets 

661 if kind in ('all', 'unix'): 

662 ret.extend([_common.pconn(*conn) for conn in 

663 self._get_unix_sockets(self.pid)]) 

664 return ret 

665 

666 nt_mmap_grouped = namedtuple('mmap', 'path rss anon locked') 

667 nt_mmap_ext = namedtuple('mmap', 'addr perms path rss anon locked') 

668 

669 @wrap_exceptions 

670 def memory_maps(self): 

671 def toaddr(start, end): 

672 return '%s-%s' % (hex(start)[2:].strip('L'), 

673 hex(end)[2:].strip('L')) 

674 

675 procfs_path = self._procfs_path 

676 retlist = [] 

677 try: 

678 rawlist = cext.proc_memory_maps(self.pid, procfs_path) 

679 except OSError as err: 

680 if err.errno == errno.EOVERFLOW and not IS_64_BIT: 

681 # We may get here if we attempt to query a 64bit process 

682 # with a 32bit python. 

683 # Error originates from read() and also tools like "cat" 

684 # fail in the same way (!). 

685 # Since there simply is no way to determine CPU times we 

686 # return 0.0 as a fallback. See: 

687 # https://github.com/giampaolo/psutil/issues/857 

688 return [] 

689 else: 

690 raise 

691 hit_enoent = False 

692 for item in rawlist: 

693 addr, addrsize, perm, name, rss, anon, locked = item 

694 addr = toaddr(addr, addrsize) 

695 if not name.startswith('['): 

696 try: 

697 name = os.readlink( 

698 '%s/%s/path/%s' % (procfs_path, self.pid, name)) 

699 except OSError as err: 

700 if err.errno == errno.ENOENT: 

701 # sometimes the link may not be resolved by 

702 # readlink() even if it exists (ls shows it). 

703 # If that's the case we just return the 

704 # unresolved link path. 

705 # This seems an incosistency with /proc similar 

706 # to: http://goo.gl/55XgO 

707 name = '%s/%s/path/%s' % (procfs_path, self.pid, name) 

708 hit_enoent = True 

709 else: 

710 raise 

711 retlist.append((addr, perm, name, rss, anon, locked)) 

712 if hit_enoent: 

713 self._assert_alive() 

714 return retlist 

715 

716 @wrap_exceptions 

717 def num_fds(self): 

718 return len(os.listdir("%s/%s/fd" % (self._procfs_path, self.pid))) 

719 

720 @wrap_exceptions 

721 def num_ctx_switches(self): 

722 return _common.pctxsw( 

723 *cext.proc_num_ctx_switches(self.pid, self._procfs_path)) 

724 

725 @wrap_exceptions 

726 def wait(self, timeout=None): 

727 return _psposix.wait_pid(self.pid, timeout, self._name)