llnnmc
12/15/2019 - 12:49 PM

procinfo.py

procinfo.py

#!/usr/bin/env python
#-*- coding:utf-8 -*-

"""
Print detailed information about a process.
$ python procinfo.py <pid>
pid         5396
name        pycharm64.exe
parent      11896 (explorer.exe)
exe         C:\\Program Files\\JetBrains\\PyCharm 2018.3.2\\bin\\pycharm64.exe
cwd         C:\\Users\\Administrator\\Desktop
cmdline     C:\\Program Files\\JetBrains\\PyCharm 2018.3.2\\bin\\pycharm64.exe
started     2019-02-23 16:00
cpu-tspent  22:02.62
cpu-times   user=966.796875, system=355.828125, children_user=0.0, children_system=0.0
cpu-affinity [0, 1, 2, 3, 4, 5, 6, 7]
memory      rss=1.2G, vms=1.3G, num_page_faults=1.1M, peak_wset=1.3G, wset=1.2G, peak_paged_pool=427.9K, paged_pool=413.4K, peak_nonpaged_pool=68.4K, nonpaged_pool=64.3K, pagefile=1.3G, peak_pagefile=1.4G, private=1.3G
memory %    15.7
user        PC2018\\Administrator
status      running
nice        Priority.NORMAL_PRIORITY_CLASS
ionice      2
num-threads 49
num-handles 1071
I/O         read_count=150.9K, write_count=15.3K, read_bytes=359.7M, write_bytes=432.3M, other_count=354.8K, other_bytes=11.8M
ctx-switches voluntary=4400335, involuntary=0
children    PID    NAME
            9016   fsnotifier64.exe
open-files  PATH
            C:\\Program Files\\JetBrains\\PyCharm 2018.3.2\\jre64\\lib\\rt.jar
            C:\\Program Files\\WindowsApps\\Microsoft.LanguageExperiencePackzh-cn_17134.25.33.0_neutral__8wekyb3d8bbwe\\Windows\\System32\\zh-CN\\shell32.dll.mui
            C:\\Users\\Administrator\\.PyCharm2018.3\\system\\caches\\attrib.dat.storageData
            C:\\Users\\Administrator\\.PyCharm2018.3\\system\\LocalHistory\\changes.storageData
            [...]
connections PROTO LOCAL ADDR                REMOTE ADDR               STATUS
            TCP   127.0.0.1:6942            *:*                       LISTEN
            TCP   127.0.0.1:32039           127.0.0.1:32038           ESTABLISHED
            TCP   127.0.0.1:32037           127.0.0.1:32036           ESTABLISHED
            TCP   127.0.0.1:32038           127.0.0.1:32039           ESTABLISHED
            TCP   127.0.0.1:32036           127.0.0.1:32037           ESTABLISHED
            TCP   127.0.0.1:63342           *:*                       LISTEN
            TCP   0.0.0.0:32044             *:*                       LISTEN
threads     TID           USER       SYSTEM
            4372      0.296875      0.15625
            15520        1.875       0.1875
            15776     1.921875     0.234375
            7972       1.84375     0.109375
            [...]
            total=49
environ     NAME                      VALUE
            ALLUSERSPROFILE           C:\\ProgramData
            APPDATA                   C:\\Users\\Administrator\\AppData\\Roaming
            CATALINA_HOME             D:\\yizit\\software\\saiku-server\\tomcat
            CLASSPATH                 .;C:\\Java\\jdk1.8.0_191\\lib\\tools.jar;C:\\Java\\jdk1.8.0_191\\lib\\dt.jar
            [...]
mem-maps    RSS      PATH
            20.2M    C:\\Windows\\System32\\shell32.dll
            12.2M    C:\\Windows\\System32\\ieframe.dll
            8.3M     C:\\Program Files\\JetBrains\\PyCharm 2018.3.2\\jre64\\bin\\server\\jvm.dll
            7.1M     C:\\Windows\\System32\\windows.storage.dll
            [...]
"""

import argparse
import datetime
import socket
import sys
import psutil

ACCESS_DENIED = ''
NON_VERBOSE_ITERATIONS = 4
RLIMITS_MAP = {
    "RLIMIT_AS": "virtualmem",
    "RLIMIT_CORE": "coredumpsize",
    "RLIMIT_CPU": "cputime",
    "RLIMIT_DATA": "datasize",
    "RLIMIT_FSIZE": "filesize",
    "RLIMIT_LOCKS": "locks",
    "RLIMIT_MEMLOCK": "memlock",
    "RLIMIT_MSGQUEUE": "msgqueue",
    "RLIMIT_NICE": "nice",
    "RLIMIT_NOFILE": "openfiles",
    "RLIMIT_NPROC": "maxprocesses",
    "RLIMIT_RSS": "rss",
    "RLIMIT_RTPRIO": "realtimeprio",
    "RLIMIT_RTTIME": "rtimesched",
    "RLIMIT_SIGPENDING": "sigspending",
    "RLIMIT_STACK": "stack",
}

def convert_bytes(n):
    symbols = ('K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
    prefix = {}
    for i, s in enumerate(symbols):
        prefix[s] = 1 << (i + 1) * 10
    for s in reversed(symbols):
        if n >= prefix[s]:
            value = float(n) / prefix[s]
            return '%.1f%s' % (value, s)
    return "%sB" % n

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, bytes2human=False):
    if nt == ACCESS_DENIED:
        return ""
    if not bytes2human:
        return ", ".join(["%s=%s" % (x, getattr(nt, x)) for x in nt._fields])
    else:
        return ", ".join(["%s=%s" % (x, convert_bytes(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'], bytes2human=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'], bytes2human=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 % (convert_bytes(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")
    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())