| #!/usr/bin/env python3 |
| |
| # Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """ |
| Print detailed information about a process. |
| Author: Giampaolo Rodola' <g.rodola@gmail.com> |
| |
| $ python3 scripts/procinfo.py |
| pid 4600 |
| name chrome |
| parent 4554 (bash) |
| exe /opt/google/chrome/chrome |
| cwd /home/giampaolo |
| cmdline /opt/google/chrome/chrome |
| started 2016-09-19 11:12 |
| cpu-tspent 27:27.68 |
| cpu-times user=8914.32, system=3530.59, |
| children_user=1.46, children_system=1.31 |
| cpu-affinity [0, 1, 2, 3, 4, 5, 6, 7] |
| memory rss=520.5M, vms=1.9G, shared=132.6M, text=95.0M, lib=0B, |
| data=816.5M, dirty=0B |
| memory % 3.26 |
| user giampaolo |
| uids real=1000, effective=1000, saved=1000 |
| uids real=1000, effective=1000, saved=1000 |
| terminal /dev/pts/2 |
| status sleeping |
| nice 0 |
| ionice class=IOPriority.IOPRIO_CLASS_NONE, value=0 |
| num-threads 47 |
| num-fds 379 |
| I/O read_count=96.6M, write_count=80.7M, |
| read_bytes=293.2M, write_bytes=24.5G |
| ctx-switches voluntary=30426463, involuntary=460108 |
| children PID NAME |
| 4605 cat |
| 4606 cat |
| 4609 chrome |
| 4669 chrome |
| open-files PATH |
| /opt/google/chrome/icudtl.dat |
| /opt/google/chrome/snapshot_blob.bin |
| /opt/google/chrome/natives_blob.bin |
| /opt/google/chrome/chrome_100_percent.pak |
| [...] |
| connections PROTO LOCAL ADDR REMOTE ADDR STATUS |
| UDP 10.0.0.3:3693 *:* NONE |
| TCP 10.0.0.3:55102 172.217.22.14:443 ESTABLISHED |
| UDP 10.0.0.3:35172 *:* NONE |
| TCP 10.0.0.3:32922 172.217.16.163:443 ESTABLISHED |
| UDP :::5353 *:* NONE |
| UDP 10.0.0.3:59925 *:* NONE |
| threads TID USER SYSTEM |
| 11795 0.7 1.35 |
| 11796 0.68 1.37 |
| 15887 0.74 0.03 |
| 19055 0.77 0.01 |
| [...] |
| total=47 |
| res-limits RLIMIT SOFT HARD |
| virtualmem infinity infinity |
| coredumpsize 0 infinity |
| cputime infinity infinity |
| datasize infinity infinity |
| filesize infinity infinity |
| locks infinity infinity |
| memlock 65536 65536 |
| msgqueue 819200 819200 |
| nice 0 0 |
| openfiles 8192 65536 |
| maxprocesses 63304 63304 |
| rss infinity infinity |
| realtimeprio 0 0 |
| rtimesched infinity infinity |
| sigspending 63304 63304 |
| stack 8388608 infinity |
| mem-maps RSS PATH |
| 381.4M [anon] |
| 62.8M /opt/google/chrome/chrome |
| 15.8M /home/giampaolo/.config/google-chrome/Default/History |
| 6.6M /home/giampaolo/.config/google-chrome/Default/Favicons |
| [...] |
| """ |
| |
| import argparse |
| import datetime |
| import socket |
| import sys |
| |
| import psutil |
| from psutil._common import bytes2human |
| |
| |
| ACCESS_DENIED = '' |
| NON_VERBOSE_ITERATIONS = 4 |
| RLIMITS_MAP = { |
| "RLIMIT_AS": "virtualmem", |
| "RLIMIT_CORE": "coredumpsize", |
| "RLIMIT_CPU": "cputime", |
| "RLIMIT_DATA": "datasize", |
| "RLIMIT_FSIZE": "filesize", |
| "RLIMIT_MEMLOCK": "memlock", |
| "RLIMIT_MSGQUEUE": "msgqueue", |
| "RLIMIT_NICE": "nice", |
| "RLIMIT_NOFILE": "openfiles", |
| "RLIMIT_NPROC": "maxprocesses", |
| "RLIMIT_NPTS": "pseudoterms", |
| "RLIMIT_RSS": "rss", |
| "RLIMIT_RTPRIO": "realtimeprio", |
| "RLIMIT_RTTIME": "rtimesched", |
| "RLIMIT_SBSIZE": "sockbufsize", |
| "RLIMIT_SIGPENDING": "sigspending", |
| "RLIMIT_STACK": "stack", |
| "RLIMIT_SWAP": "swapuse", |
| } |
| |
| |
| def print_(a, b): |
| if sys.stdout.isatty() and psutil.POSIX: |
| fmt = '\x1b[1;32m%-13s\x1b[0m %s' % (a, b) |
| else: |
| fmt = '%-11s %s' % (a, b) |
| print(fmt) |
| |
| |
| def str_ntuple(nt, convert_bytes=False): |
| if nt == ACCESS_DENIED: |
| return "" |
| if not convert_bytes: |
| return ", ".join(["%s=%s" % (x, getattr(nt, x)) for x in nt._fields]) |
| else: |
| return ", ".join(["%s=%s" % (x, bytes2human(getattr(nt, x))) |
| for x in nt._fields]) |
| |
| |
| def run(pid, verbose=False): |
| try: |
| proc = psutil.Process(pid) |
| pinfo = proc.as_dict(ad_value=ACCESS_DENIED) |
| except psutil.NoSuchProcess as err: |
| sys.exit(str(err)) |
| |
| # collect other proc info |
| with proc.oneshot(): |
| try: |
| parent = proc.parent() |
| if parent: |
| parent = '(%s)' % parent.name() |
| else: |
| parent = '' |
| except psutil.Error: |
| parent = '' |
| try: |
| pinfo['children'] = proc.children() |
| except psutil.Error: |
| pinfo['children'] = [] |
| if pinfo['create_time']: |
| started = datetime.datetime.fromtimestamp( |
| pinfo['create_time']).strftime('%Y-%m-%d %H:%M') |
| else: |
| started = ACCESS_DENIED |
| |
| # here we go |
| print_('pid', pinfo['pid']) |
| print_('name', pinfo['name']) |
| print_('parent', '%s %s' % (pinfo['ppid'], parent)) |
| print_('exe', pinfo['exe']) |
| print_('cwd', pinfo['cwd']) |
| print_('cmdline', ' '.join(pinfo['cmdline'])) |
| print_('started', started) |
| |
| cpu_tot_time = datetime.timedelta(seconds=sum(pinfo['cpu_times'])) |
| cpu_tot_time = "%s:%s.%s" % ( |
| cpu_tot_time.seconds // 60 % 60, |
| str((cpu_tot_time.seconds % 60)).zfill(2), |
| str(cpu_tot_time.microseconds)[:2]) |
| print_('cpu-tspent', cpu_tot_time) |
| print_('cpu-times', str_ntuple(pinfo['cpu_times'])) |
| if hasattr(proc, "cpu_affinity"): |
| print_("cpu-affinity", pinfo["cpu_affinity"]) |
| if hasattr(proc, "cpu_num"): |
| print_("cpu-num", pinfo["cpu_num"]) |
| |
| print_('memory', str_ntuple(pinfo['memory_info'], convert_bytes=True)) |
| print_('memory %', round(pinfo['memory_percent'], 2)) |
| print_('user', pinfo['username']) |
| if psutil.POSIX: |
| print_('uids', str_ntuple(pinfo['uids'])) |
| if psutil.POSIX: |
| print_('uids', str_ntuple(pinfo['uids'])) |
| if psutil.POSIX: |
| print_('terminal', pinfo['terminal'] or '') |
| |
| print_('status', pinfo['status']) |
| print_('nice', pinfo['nice']) |
| if hasattr(proc, "ionice"): |
| try: |
| ionice = proc.ionice() |
| except psutil.Error: |
| pass |
| else: |
| if psutil.WINDOWS: |
| print_("ionice", ionice) |
| else: |
| print_("ionice", "class=%s, value=%s" % ( |
| str(ionice.ioclass), ionice.value)) |
| |
| print_('num-threads', pinfo['num_threads']) |
| if psutil.POSIX: |
| print_('num-fds', pinfo['num_fds']) |
| if psutil.WINDOWS: |
| print_('num-handles', pinfo['num_handles']) |
| |
| if 'io_counters' in pinfo: |
| print_('I/O', str_ntuple(pinfo['io_counters'], convert_bytes=True)) |
| if 'num_ctx_switches' in pinfo: |
| print_("ctx-switches", str_ntuple(pinfo['num_ctx_switches'])) |
| if pinfo['children']: |
| template = "%-6s %s" |
| print_("children", template % ("PID", "NAME")) |
| for child in pinfo['children']: |
| try: |
| print_('', template % (child.pid, child.name())) |
| except psutil.AccessDenied: |
| print_('', template % (child.pid, "")) |
| except psutil.NoSuchProcess: |
| pass |
| |
| if pinfo['open_files']: |
| print_('open-files', 'PATH') |
| for i, file in enumerate(pinfo['open_files']): |
| if not verbose and i >= NON_VERBOSE_ITERATIONS: |
| print_("", "[...]") |
| break |
| print_('', file.path) |
| else: |
| print_('open-files', '') |
| |
| if pinfo['connections']: |
| template = '%-5s %-25s %-25s %s' |
| print_('connections', |
| template % ('PROTO', 'LOCAL ADDR', 'REMOTE ADDR', 'STATUS')) |
| for conn in pinfo['connections']: |
| if conn.type == socket.SOCK_STREAM: |
| type = 'TCP' |
| elif conn.type == socket.SOCK_DGRAM: |
| type = 'UDP' |
| else: |
| type = 'UNIX' |
| lip, lport = conn.laddr |
| if not conn.raddr: |
| rip, rport = '*', '*' |
| else: |
| rip, rport = conn.raddr |
| print_('', template % ( |
| type, |
| "%s:%s" % (lip, lport), |
| "%s:%s" % (rip, rport), |
| conn.status)) |
| else: |
| print_('connections', '') |
| |
| if pinfo['threads'] and len(pinfo['threads']) > 1: |
| template = "%-5s %12s %12s" |
| print_('threads', template % ("TID", "USER", "SYSTEM")) |
| for i, thread in enumerate(pinfo['threads']): |
| if not verbose and i >= NON_VERBOSE_ITERATIONS: |
| print_("", "[...]") |
| break |
| print_('', template % thread) |
| print_('', "total=%s" % len(pinfo['threads'])) |
| else: |
| print_('threads', '') |
| |
| if hasattr(proc, "rlimit"): |
| res_names = [x for x in dir(psutil) if x.startswith("RLIMIT")] |
| resources = [] |
| for res_name in res_names: |
| try: |
| soft, hard = proc.rlimit(getattr(psutil, res_name)) |
| except psutil.AccessDenied: |
| pass |
| else: |
| resources.append((res_name, soft, hard)) |
| if resources: |
| template = "%-12s %15s %15s" |
| print_("res-limits", template % ("RLIMIT", "SOFT", "HARD")) |
| for res_name, soft, hard in resources: |
| if soft == psutil.RLIM_INFINITY: |
| soft = "infinity" |
| if hard == psutil.RLIM_INFINITY: |
| hard = "infinity" |
| print_('', template % ( |
| RLIMITS_MAP.get(res_name, res_name), soft, hard)) |
| |
| if hasattr(proc, "environ") and pinfo['environ']: |
| template = "%-25s %s" |
| print_("environ", template % ("NAME", "VALUE")) |
| for i, k in enumerate(sorted(pinfo['environ'])): |
| if not verbose and i >= NON_VERBOSE_ITERATIONS: |
| print_("", "[...]") |
| break |
| print_("", template % (k, pinfo['environ'][k])) |
| |
| if pinfo.get('memory_maps', None): |
| template = "%-8s %s" |
| print_("mem-maps", template % ("RSS", "PATH")) |
| maps = sorted(pinfo['memory_maps'], key=lambda x: x.rss, reverse=True) |
| for i, region in enumerate(maps): |
| if not verbose and i >= NON_VERBOSE_ITERATIONS: |
| print_("", "[...]") |
| break |
| print_("", template % (bytes2human(region.rss), region.path)) |
| |
| |
| def main(argv=None): |
| parser = argparse.ArgumentParser( |
| description="print information about a process") |
| parser.add_argument("pid", type=int, help="process pid", nargs='?') |
| parser.add_argument('--verbose', '-v', action='store_true', |
| help="print more info") |
| args = parser.parse_args() |
| run(args.pid, args.verbose) |
| |
| |
| if __name__ == '__main__': |
| sys.exit(main()) |